قصة خطأ واحد: لماذا mysql_* هو من الماضي
\n\nتخيل: أنت تكتب موقعًا على PHP، وتتصل بقاعدة بيانات MySQL وتستخدم الدالة المألوفة من الجامعة mysql_query(). كل شيء يعمل، الموقع يعيش، المستخدمون سعداء. ولكن في يوم من الأيام تلاحظ تحذيرًا غريبًا في السجلات: «Deprecated: mysql_connect(): The mysql extension is deprecated and will be removed in the future». هذا ليس مجرد تذمر من المفسر - إنه علم أحمر لا يمكن تجاهله.
على Stack Overflow، جمع سؤال «Why shouldn't I use mysql_* functions in PHP?» آلاف المشاهدات ومئات التعليقات. وهذا ليس من قبيل الصدفة. في هذه المقالة، سنحلل لماذا أصبحت دوال mysql_* (mysql_connect, mysql_query, mysql_fetch_array وغيرها) العدو رقم واحد لمطوري PHP، وسنعرض بأمثلة كود حقيقية ما الذي يجب استبدالها به، وسنقدم خطة ترحيل خطوة بخطوة.
\n\nما هي mysql_* ولماذا تمت إزالتها؟
\n\nظهر إضافة mysql_* (MySQL الأصلي) في PHP 2.0 في عام 1997 البعيد. كانت بسيطة وبديهية: استدعاء mysql_connect()، تنفيذ استعلام عبر mysql_query()، الحصول على النتيجة. ولكن لهذه البساطة كان جانب مظلم.
ابتداءً من PHP 5.5.0، تم وضع علامة قديمة (deprecated) على جميع دوال mysql_*، وفي PHP 7.0 تم حذفها بالكامل. إذا حاولت استخدام mysql_connect() على PHP 7+، ستحصل على خطأ فادح: Call to undefined function mysql_connect(). السبب ليس نزوة من مطوري PHP، بل مشاكل أساسية في الأمان والهندسة المعمارية.
ثلاثة أسباب رئيسية للتخلي عن mysql_*
\n\n- \n
- حقن SQL بدون حماية - الدوال لا تدعم الاستعلامات المحضرة (prepared statements)، مما يجعل التعقيم اليدوي إلزاميًا، ولكنه خطوة غالبًا ما تُنسى. \n
- هندسة معمارية قديمة - لا يوجد دعم للأسلوب الكائني، والإجراءات المخزنة، والاستعلامات المتعددة، والمعاملات. \n
- غياب التطوير - لم يتم تحديث الإضافة لسنوات، بينما تغير MySQL (على سبيل المثال، ظهرت أنواع بيانات جديدة، وترميزات، وآليات مصادقة). \n
مثال حي: كيف تقتل mysql_* موقعك
\n\nدعنا نلقي نظرة على الكود الكلاسيكي الذي لا يزال موجودًا في الكتب المدرسية القديمة والمنتديات:
\n\n<?php\n// كود قديم - لا تستخدمه!\n$link = mysql_connect('localhost', 'root', 'password');\nmysql_select_db('users_db', $link);\n\n$user_input = $_GET['username']; // المستخدم يدخل: ' OR 1=1 --\n$query = "SELECT * FROM users WHERE username = '$user_input'";\n$result = mysql_query($query, $link);\n\nwhile ($row = mysql_fetch_assoc($result)) {\n echo "مرحبًا، " . $row['username'] . "<br>";\n}\n?>\n\nما المشكلة؟ إذا قام المهاجم بإدخال القيمة ' OR 1=1 -- في المعامل username، سيتحول الاستعلام إلى:
SELECT * FROM users WHERE username = '' OR 1=1 --\n\nالشرطة المزدوجة -- ستعلق باقي الاستعلام، والشرط 1=1 صحيح دائمًا. ونتيجة لذلك، ستحصل على جميع المستخدمين في قاعدة البيانات، بما في ذلك كلمات المرور والبريد الإلكتروني والمعلومات السرية الأخرى. هذه ليست نظرية - هذا ثغرة حقيقية تخترق آلاف المواقع كل يوم.
بالطبع، يمكن استخدام mysql_real_escape_string()، ولكن:
- \n
- غالبًا ما ينسى المطورون استدعاءها. \n
- لا تحمي من جميع أنواع الهجمات (مثل الهجمات باستخدام الترميزات). \n
- يصبح الكود قذرًا ويصعب قراءته. \n
ما الذي يجب استبدال mysql_* به: طريقتان حديثتان
\n\nفي PHP الحديثة، هناك إضافتان جديرتان بالثقة للعمل مع MySQL: mysqli (MySQL Improved) و PDO (PHP Data Objects). كلاهما يدعمان