it-swarm.asia

لماذا لا ينبغي علي استخدام وظائف mysql_ * في PHP؟

ما هي الأسباب التقنية وراء عدم استخدام وظائف mysql_*؟ (على سبيل المثال ، mysql_query() أو mysql_connect() أو mysql_real_escape_string()

لماذا يجب علي استخدام شيء آخر حتى لو كانوا يعملون على موقعي؟

إذا لم يعملوا على موقعي ، فلماذا أحصل على أخطاء مثل

تحذير: mysql_connect (): لا يوجد مثل هذا الملف أو الدليل

2362
Madara Uchiha

ملحق MySQL:

  • ليس قيد التطوير النشط
  • هل رسميًا تم إهمالهاعتبارًا من PHP 5.5 (تم إصداره في يونيو 2013).
  • تمإزالته كليًا اعتبارًا من PHP 7.0 (تم إصداره في ديسمبر 2015)
    • هذا يعني أنه اعتبارًا من 31 ديسمبر 2018 لن يكون موجودًا في أي إصدار معتمد من PHP. حاليًا ، لا يحصل إلا على تحديثات {الأمان.
  • يفتقد إلى OO واجهة
  • لا يدعم:
    • استعلامات غير محظورة وغير متزامنة
    • البيانات المعدة أو الاستعلامات ذات المعاملات
    • الإجراءات المخزنة
    • بيانات متعددة
    • المعاملات
    • طريقة مصادقة كلمة المرور "الجديدة" (في الوضع الافتراضي في MySQL 5.6 ؛ مطلوب في 5.7)
    • كل الوظائف في MySQL 5.1

نظرًا لأنه تم إهماله ، فإن استخدامه يجعل الكود أقل إثباتًا في المستقبل.

يعد عدم وجود دعم للبيانات المعدة أهمية خاصة لأنها توفر طريقة أكثر وضوحًا وأقل عرضة للخطأ في الهروب من البيانات الخارجية والاقتباس منها بدلاً من الهروب يدويًا باستخدام استدعاء دالة منفصل.

انظر مقارنة امتدادات SQL .

1973
Quentin

يوفر PHP ثلاثة واجهات برمجة تطبيقات مختلفة للاتصال MySQL. هذه هي mysql (تمت إزالتها اعتبارًا من PHP 7) و mysqli و و PDO ملحقات.

تستخدم وظائف mysql_* لتكون شائعة للغاية ، ولكن لم يتم تشجيع استخدامها بعد الآن. يناقش فريق التوثيق الموقف الأمني ​​لقاعدة البيانات ، ويعد تثقيف المستخدمين بالابتعاد عن امتداد ext/mysql شائع الاستخدام جزءًا من هذا (حدد php.internals: deprecating ext/mysql).

وأحدث PHP فريق المطورين اتخذ قرارًا بإنشاء E_DEPRECATED أخطاء عند اتصال المستخدمين بـ MySQL ، سواء من خلال mysql_connect() أو mysql_pconnect() أو وظيفة الاتصال الضمنية المضمنة في ext/mysql.

ext/mysqlwas تم إهمالها رسميًا اعتبارًا من PHP 5.5 وتمت إزالتها تمت إزالتها اعتبارًا من PHP 7 .

انظر المربع الأحمر؟

عندما تنتقل إلى أي صفحة من صفحات دليل الوظائف mysql_* ، فإنك ترى مربعًا أحمر ، موضحًا أنه لا ينبغي استخدامه بعد الآن.

لماذا ا


لا يتعلق الابتعاد عن ext/mysql بالأمان فحسب ، بل يتعلق أيضًا بالوصول إلى جميع ميزات قاعدة بيانات MySQL.

تم إنشاء ext/mysql لـ MySQL 3.23 وحصلت فقط على عدد قليل جدًا من الإضافات منذ ذلك الحين مع الحفاظ في الغالب على التوافق مع هذا الإصدار القديم مما يجعل الحفاظ على الشفرة أصعب قليلاً. تتضمن الميزات المفقودة غير المدعومة بواسطة ext/mysql: (من PHP manual).

سبب عدم استخدام دالة mysql_*:

  • ليس تحت التطوير النشط
  • تمت الإزالة اعتبارًا من PHP 7
  • يفتقد إلى OO واجهة
  • لا يدعم استعلامات غير متزامنة غير محظورة
  • لا يدعم البيانات المعدة أو الاستفسارات ذات المعلمات
  • لا يدعم الإجراءات المخزنة
  • لا يدعم عبارات متعددة
  • لا يدعم المعاملات
  • لا يدعم جميع الوظائف في MySQL 5.1

أعلى نقطة مقتبسة من إجابة كوينتن

يعد عدم وجود دعم للبيانات المعدة أهمية خاصة لأنها توفر طريقة أكثر وضوحًا وأقل عرضة للخطأ في الهروب من البيانات الخارجية والاقتباس منها بدلاً من الهروب يدويًا باستخدام استدعاء دالة منفصل.

انظر مقارنة ملحقات SQL .


قمع تحذيرات الإهمال

أثناء تحويل الشفرة إلى MySQLi/PDO ، يمكن كبح أخطاء E_DEPRECATED عن طريق تعيين error_reporting في php.ini لاستبعاد E_DEPRECATED:

error_reporting = E_ALL ^ E_DEPRECATED

لاحظ أن هذا سيؤدي أيضًا إلى إخفاء تحذيرات إهمال أخرى ، والتي قد تكون لأشياء أخرى غير MySQL. (من PHP manual)

المقالة ( PDO مقابل MySQLi: ما الذي يجب عليك استخدامه؟ بواسطة Dejan Marjanovic سوف تساعدك على الاختيار.

وأفضل طريقة هي PDO ، وأنا أكتب الآن تعليميًا بسيطًا PDO.


البرنامج التعليمي PDO بسيط وقصير


السؤال الأول في ذهني هو: ما هو "شركة تنمية نفط عمان"؟

A. " PDO - PHP كائنات البيانات - هي طبقة الوصول إلى قاعدة البيانات التي توفر طريقة موحدة للوصول إلى قواعد بيانات متعددة."

alt text


الاتصال الخلية

من خلال وظيفة mysql_* أو يمكننا أن نقولها بالطريقة القديمة (تم إهمالها في PHP 5.5 وما فوق)

$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);

باستخدام PDO: كل ما عليك فعله هو إنشاء كائن PDO جديد. يقبل المُنشئ المعلمات لتحديد مصدر قاعدة البيانات PDO في أغلب الأحيان ، يستغرق مُنشئ الرسالة أربعة معلمات هي DSN (اسم مصدر البيانات) واختيارياً username ، password.

أعتقد هنا أنك معتاد على الجميع باستثناء DSN؛ هذا جديد في PDO. DSN هو في الأساس سلسلة من الخيارات التي تخبر PDO عن برنامج التشغيل المطلوب استخدامه وتفاصيل الاتصال. لمزيد من المرجع ، تحقق PDO MySQL DSN .

$db = new PDO('mysql:Host=localhost;dbname=testdb;charset=utf8', 'username', 'password');

ملاحظة: يمكنك أيضًا استخدام charset=UTF-8 ، لكن في بعض الأحيان يتسبب في حدوث خطأ ، لذلك من الأفضل استخدام utf8.

إذا كان هناك أي خطأ في الاتصال ، فسيؤدي ذلك إلى رمي كائن PDOException يمكن اكتشافه للتعامل مع Exception بشكل أكبر.

قراءة جيدة : الاتصالات وإدارة الاتصال ¶

يمكنك أيضًا تمرير العديد من خيارات برنامج التشغيل كصفيف إلى المعلمة الرابعة. أوصي بتمرير المعلمة التي تضع PDO في وضع الاستثناء. نظرًا لأن بعض برامج التشغيل PDO لا تدعم العبارات المعدة سلفًا ، لذلك PDO تقوم بمحاكاة الإعداد. كما يتيح لك تمكين هذا المحاكاة يدويًا. لاستخدام البيانات المعدة من جانب الخادم الأصلي ، يجب عليك تعيينها بوضوح false.

والآخر هو إيقاف تشغيل محاكاة التهيئة التي يتم تمكينها في برنامج التشغيل MySQL بشكل افتراضي ، ولكن يجب إيقاف تشغيل إعداد المضاهاة لاستخدام PDO بأمان.

سأشرح لاحقًا لماذا يجب إيقاف تشغيل المحاكاة. لمعرفة السبب يرجى مراجعة هذا المنصب .

يكون قابلاً للاستخدام فقط إذا كنت تستخدم إصدارًا قديمًا من MySQL الذي لا أوصي به.

فيما يلي مثال لكيفية القيام بذلك:

$db = new PDO('mysql:Host=localhost;dbname=testdb;charset=UTF-8', 
              'username', 
              'password',
              array(PDO::ATTR_EMULATE_PREPARES => false,
              PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

هل يمكننا تعيين سمات بعد بناء PDO؟

نعم ، يمكننا أيضًا تعيين بعض السمات بعد بناء PDO باستخدام طريقة setAttribute:

$db = new PDO('mysql:Host=localhost;dbname=testdb;charset=UTF-8', 
              'username', 
              'password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

معالجة الأخطاء


تعتبر معالجة الأخطاء أسهل بكثير في PDO من mysql_*.

من الممارسات الشائعة عند استخدام mysql_*:

//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));

OR die() ليست طريقة جيدة للتعامل مع الخطأ لأننا لا نستطيع التعامل مع الشيء في die. سينتهي البرنامج النصي بشكل مفاجئ ، ثم يردد الخطأ على الشاشة الذي لا ترغب عادةً في إظهاره للمستخدمين النهائيين ، ويسمح للمتسللين الداميين باكتشاف المخطط الخاص بك. بالتناوب ، غالبًا ما يمكن استخدام قيم الإرجاع لوظائف mysql_* بالاقتران مع mysql_error () لمعالجة الأخطاء.

PDO يقدم حلاً أفضل: استثناءات. يجب أن يتم تغليف أي شيء نفعله بـ PDO في كتلة try-catch. يمكننا فرض PDO في أحد أوضاع الخطأ الثلاثة عن طريق تعيين سمة وضع الخطأ. ثلاثة أوضاع معالجة الأخطاء أدناه.

  • PDO::ERRMODE_SILENT. إنه مجرد إعداد رموز الأخطاء ويتصرف إلى حد كبير مثل mysql_* حيث يجب عليك التحقق من كل نتيجة ثم انظر إلى $db->errorInfo(); للحصول على تفاصيل الخطأ.
  • PDO::ERRMODE_WARNING رفع E_WARNING. (تحذيرات وقت التشغيل (أخطاء غير مميتة). لم يتم إيقاف تنفيذ البرنامج النصي.)
  • PDO::ERRMODE_EXCEPTION: استثناءات رمي. إنه يمثل خطأً أثارته شركة تنمية نفط عمان. يجب ألا ترمي PDOException من الكود الخاص بك. راجع استثناءات لمزيد من المعلومات حول الاستثناءات في PHP. يتصرف إلى حد كبير مثل or die(mysql_error()); ، عندما لا يتم اكتشافه. ولكن على عكس or die() ، يمكن اكتشاف PDOException ومعالجته بأمان إذا اخترت القيام بذلك.

قراءة جيدة :

مثل:

$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

ويمكنك لفه بـ try-catch ، مثل ما يلي:

try {
    //Connect as appropriate as above
    $db->query('hi'); //Invalid query!
} 
catch (PDOException $ex) {
    echo "An Error occured!"; //User friendly message/message you want to show to user
    some_logging_function($ex->getMessage());
}

ليس لديك للتعامل مع try-catch الآن. يمكنك التقاطها في أي وقت مناسب ، لكنني أوصي بشدة باستخدام try-catch. أيضًا قد يكون من المنطقي أن نلاحظها خارج الوظيفة التي تستدعي عناصر PDO:

function data_fun($db) {
    $stmt = $db->query("SELECT * FROM table");
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

//Then later
try {
    data_fun($db);
}
catch(PDOException $ex) {
    //Here you can handle error and show message/perform action you want.
}

أيضًا ، يمكنك التعامل مع or die() أو يمكننا أن نقول مثل mysql_* ، لكن ستكون متنوعة بالفعل. يمكنك إخفاء رسائل الخطأ الخطيرة في الإنتاج عن طريق تشغيل display_errors off وقراءة سجل الأخطاء الخاص بك فقط.

الآن ، بعد قراءة كل الأشياء المذكورة أعلاه ، ربما تفكر في ما: ما هو أنه عندما أريد فقط أن أبدأ في تعليل SELECT أو INSERT أو UPDATE أو DELETE؟ لا تقلق ، هنا نذهب:


اختيار البيانات

PDO select image

إذن ما تفعله في mysql_* هو:

<?php
$result = mysql_query('SELECT * from table') or die(mysql_error());

$num_rows = mysql_num_rows($result);

while($row = mysql_fetch_assoc($result)) {
    echo $row['field1'];
}

الآن في PDO ، يمكنك القيام بذلك مثل:

<?php
$stmt = $db->query('SELECT * FROM table');

while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    echo $row['field1'];
}

أو

<?php
$stmt = $db->query('SELECT * FROM table');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

//Use $results

ملاحظة : إذا كنت تستخدم الطريقة كما هو موضح أدناه (query()) ، فتُرجع هذه الطريقة كائن PDOStatement. لذلك إذا كنت ترغب في جلب النتيجة ، استخدمها كما هو مذكور أعلاه.

<?php
foreach($db->query('SELECT * FROM table') as $row) {
    echo $row['field1'];
}

في بيانات شركة تنمية نفط عمان ، يتم الحصول عليها عبر ->fetch() ، وهي طريقة لمعالجة العبارة الخاصة بك. قبل الاتصال بالجلب ، سيكون أفضل أسلوب هو إخبار شركة تنمية نفط عمان بالطريقة التي ترغب في جلب البيانات بها. في القسم أدناه أنا أشرح هذا.

جلب الجلب

لاحظ استخدام PDO::FETCH_ASSOC في الكود fetch() و fetchAll() أعلاه. يخبر هذا PDO بإرجاع الصفوف كصفيف اقتران بأسماء الحقول كمفاتيح. هناك العديد من أوضاع الجلب الأخرى التي سأشرحها واحدة تلو الأخرى.

بادئ ذي بدء ، أشرح كيفية تحديد وضع الجلب:

 $stmt->fetch(PDO::FETCH_ASSOC)

في أعلاه ، كنت أستخدم fetch(). تستطيع ايضا استخذام:

الآن جئت إلى جلب الوضع:

  • PDO::FETCH_ASSOC: إرجاع صفيف مفهرسة حسب اسم العمود كما تم إرجاعه في مجموعة النتائج
  • PDO::FETCH_BOTH (افتراضي): تُرجع صفيفًا مفهرسًا بكل من اسم العمود ورقم العمود المفهرس 0 كما تم إرجاعه في مجموعة النتائج

هناك المزيد من الخيارات! اقرأ عنها جميعًا في PDOStatement جلب الوثائق. .

الحصول على عدد الصفوف :

بدلاً من استخدام mysql_num_rows للحصول على عدد الصفوف التي تم إرجاعها ، يمكنك الحصول على PDOStatement والقيام rowCount() ، مثل:

<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';

الحصول على آخر معرّف مدرج

<?php
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");
$insertId = $db->lastInsertId();

إدراج وتحديث أو حذف العبارات

Insert and update PDO image

ما نقوم به في وظيفة mysql_* هو:

<?php
$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
echo mysql_affected_rows($result);

وفي pdo ، يمكن القيام بالشيء نفسه من خلال:

<?php
$affected_rows = $db->exec("UPDATE table SET field='value'");
echo $affected_rows;

في الاستعلام أعلاه PDO::exec تنفيذ عبارة SQL وإرجاع عدد الصفوف المتأثرة.

سيتم إدراج إدراج وحذف في وقت لاحق.

الطريقة المذكورة أعلاه مفيدة فقط عندما لا تستخدم المتغير في الاستعلام. ولكن عندما تحتاج إلى استخدام متغير في استعلام ما ، لا تحاول أبدًا مثل ما ورد أعلاه وهناك عبارة محضرة أو عبارة ذات معلمات هي.


البيانات المعدة

Q. ما هو البيان المعد ولماذا أحتاج إليهم؟
A. عبارة محضرة عبارة عبارة SQL تم تجميعها مسبقًا والتي يمكن تنفيذها عدة مرات عن طريق إرسال البيانات فقط إلى الخادم.

سير العمل النموذجي لاستخدام بيان معد كما يلي ( مقتبس من ويكيبيديا ثلاث نقاط ):

  1. تحضير : يتم إنشاء قالب العبارة بواسطة التطبيق وإرسالها إلى نظام إدارة قاعدة البيانات (DBMS). يتم ترك بعض القيم غير محددة ، تسمى المعلمات أو العناصر النائبة أو ربط المتغيرات (المسمى ? أدناه):

    INSERT INTO PRODUCT (name, price) VALUES (?, ?)

  2. يقوم نظام إدارة قواعد البيانات بتوزيع وتجميع وتنفيذ عملية تحسين الاستعلام في قالب البيان ، ويقوم بتخزين النتيجة دون تنفيذها.

  3. تنفيذ : في وقت لاحق ، يوفر التطبيق (أو ربط) قيم المعلمات ، وينفذ نظام إدارة قواعد البيانات العبارة (ربما تقوم بإرجاع نتيجة). قد ينفذ التطبيق العبارة عدة مرات كما تريد مع قيم مختلفة. في هذا المثال ، قد يوفر "الخبز" للمعلمة الأولى و 1.00 للمعلمة الثانية.

يمكنك استخدام بيان معد من خلال تضمين العناصر النائبة في SQL الخاص بك. يوجد ثلاثة منهم بدون مواضع (لا تحاول ذلك مع المتغير الخاص به أعلاه) ، واحد مع العناصر النائبة غير المسماة ، والآخر مع العناصر النائبة المحددة.

Q. حتى الآن ، ما يسمى العناصر النائبة وكيف يمكنني استخدامها؟
A. العناصر النائبة المسماة. استخدم أسماء وصفية مسبوقة بنقطتين بدلاً من علامات الاستفهام. نحن لا نهتم بالموضع/ترتيب القيمة في حامل مكان الاسم:

 $stmt->bindParam(':bla', $bla);

bindParam(parameter,variable,data_type,length,driver_options)

يمكنك أيضًا الربط باستخدام صفيف تنفيذ أيضًا:

<?php
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

ميزة لطيفة أخرى لأصدقاء OOP هي أن العناصر النائبة المحددة لها القدرة على إدراج كائنات مباشرة في قاعدة البيانات الخاصة بك ، مع افتراض أن الخصائص تتطابق مع الحقول المسماة. فمثلا:

class person {
    public $name;
    public $add;
    function __construct($a,$b) {
        $this->name = $a;
        $this->add = $b;
    }

}
$demo = new person('john','29 bla district');
$stmt = $db->prepare("INSERT INTO table (name, add) value (:name, :add)");
$stmt->execute((array)$demo);

Q. حتى الآن ، ما هي العناصر النائبة غير المسماة وكيف يمكنني استخدامها؟
A. هيا بنا مثال:

<?php
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->bindValue(1, $name, PDO::PARAM_STR);
$stmt->bindValue(2, $add, PDO::PARAM_STR);
$stmt->execute();

و

$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->execute(array('john', '29 bla district'));

في أعلاه ، يمكنك رؤية تلك ? بدلاً من الاسم كما في حامل مكان الاسم. الآن في المثال الأول ، نقوم بتعيين متغيرات للعناصر النائبة المختلفة ($stmt->bindValue(1, $name, PDO::PARAM_STR);). بعد ذلك ، نقوم بتخصيص قيم لتلك العناصر النائبة وتنفيذ البيان. في المثال الثاني ، ينتقل عنصر الصفيف الأول إلى ? الأول والثاني إلى ? الأول.

NOTE: في العناصر النائبة غير المسماة يجب أن نحرص على الترتيب الصحيح للعناصر في الصفيف التي نمررها إلى طريقة PDOStatement::execute().


استعلامات SELECT ، INSERT ، UPDATE ، DELETE

  1. SELECT:

    $stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
    $stmt->execute(array(':name' => $name, ':id' => $id));
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
  2. INSERT:

    $stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
    $stmt->execute(array(':field1' => $field1, ':field2' => $field2));
    $affected_rows = $stmt->rowCount();
    
  3. DELETE:

    $stmt = $db->prepare("DELETE FROM table WHERE id=:id");
    $stmt->bindValue(':id', $id, PDO::PARAM_STR);
    $stmt->execute();
    $affected_rows = $stmt->rowCount();
    
  4. UPDATE:

    $stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
    $stmt->execute(array($name, $id));
    $affected_rows = $stmt->rowCount();
    

ملاحظة:

لكن PDO و/أو MySQLi ليست آمنة تمامًا. تحقق من الإجابة {هل تعد عبارات PDO كافية لمنع حقن SQL؟ بواسطة ircmaxell . أيضًا ، أقتبس جزءًا من إجابته:

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES GBK');
$stmt = $pdo->prepare("SELECT * FROM test WHERE name = ? LIMIT 1");
$stmt->execute(array(chr(0xbf) . chr(0x27) . " OR 1=1 /*"));
1221
NullPoiиteя

أولاً ، لنبدأ بالتعليق القياسي الذي نوفره للجميع:

من فضلك ، لا تستخدم وظائف mysql_* في رمز جديد . لم تعد محفوظة ويتم إهمالها رسميا . انظر المربع الأحمر ؟ تعرف على البيانات المعدة بدلاً من ذلك ، واستخدام PDO أو MySQLi - هذه المقالة سيساعدك على تحديد أي منها. إذا اخترت PDO ، هنا هو تعليمي جيد .

دعنا نذهب من خلال هذا ، جملة جملة ، وشرح:

  • لم يعد يتم صيانتها ، ويتم إهمالها رسميًا

    هذا يعني أن المجتمع PHP ينخفض ​​تدريجياً دعمه لهذه الوظائف القديمة جدًا. من المحتمل ألا تكون موجودة في إصدار مستقبلي (حديث) من PHP! قد يؤدي الاستخدام المستمر لهذه الوظائف إلى كسر الكود في المستقبل القريب (وليس كذلك).

    جديد! - ext/mysql الآن تم إهمالها رسميًا اعتبارًا من PHP 5.5!

    أحدث! ext/mysql تمت إزالة في PHP 7.

  • بدلاً من ذلك ، يجب أن تتعلم من العبارات المعدة

    امتداد mysql_* لا يدعم بيانات محضرة ، وهو (من بين أشياء أخرى) إجراء مضاد فعال للغاية ضد حقن SQL . تم إصلاح مشكلة عدم حصانة خطيرة في التطبيقات المعتمدة على MySQL والتي تسمح للمهاجمين بالوصول إلى البرنامج النصي الخاص بك وتنفيذ أي استعلام محتمل في قاعدة البيانات الخاصة بك.

    لمزيد من المعلومات ، راجعكيف يمكنني منع حقن SQL في PHP؟

  • انظر المربع الأحمر؟

    عندما تذهب إلى أي صفحة من وظائف mysql ، سترى مربعًا أحمر ، موضحًا أنه لا ينبغي استخدامه بعد الآن.

  • استخدم إما PDO أو MySQLi

    هناك بدائل أفضل وأكثر قوة وبنية بشكل جيد ،PDO - PHP كائن قاعدة البيانات، والذي يوفر منهجًا كاملاً OOP لتفاعل قاعدة البيانات ، وMySQLi، وهو تحسين خاص MySQL.

290
Madara Uchiha

سهولة الاستعمال

سبق ذكر الأسباب التحليلية والتركيبية. للوافدين الجدد هناك حافز أكثر أهمية للتوقف عن استخدام وظائف mysql_ المؤرخة.

واجهات برمجة التطبيقات لقواعد البيانات المعاصرة هي فقط {أسهل للاستخدام.

إنه في الغالب (منضمات مرتبطة التي يمكنها تبسيط الكود. ومع البرامج التعليمية الممتازة (كما هو موضح أعلاه) الانتقال إلى PDO ليس شاقًا جدًا.

إعادة كتابة قاعدة رمز أكبر في آن واحد يستغرق وقتًا. سبب وجود هذا البديل الوسيط:

Pdo_ * المكافئ وظائف في مكان mysql_ *

باستخدام < pdo_mysql.php > يمكنك التبديل من وظائف mysql_ القديمة باستخدام الحد الأدنى من الجهد. يضيف غلاف الدالة pdo_ الذي يحل محل نظرائهم mysql_.

  1. ببساطة include_once("pdo_mysql.php"); في كل نص استدعاء يجب أن يتفاعل مع قاعدة البيانات.

  2. مسح ال mysql_ بادئة الوظيفة (everywhere واستبدله بـpdo_.

    • mysql_connect() يصبح pdo_connect()
    • mysql_query() يصبح pdo_query()
    • mysql_num_rows() يصبح pdo_num_rows()
    • mysql_insert_id() يصبح pdo_insert_id()
    • mysql_fetch_array() يصبح pdo_fetch_array()
    • mysql_fetch_assoc() يصبح pdo_fetch_assoc()
    • mysql_real_escape_string() يصبح pdo_real_escape_string()
    • وما إلى ذلك وهلم جرا...

  3. ستعمل شفرتك على حد سواء وستظل في الغالب متشابهة:

    include_once("pdo_mysql.php"); 
    
    pdo_connect("localhost", "usrABC", "pw1234567");
    pdo_select_db("test");
    
    $result = pdo_query("SELECT title, html FROM pages");  
    
    while ($row = pdo_fetch_assoc($result)) {
        print "$row[title] - $row[html]";
    }
    

إت فويلا.
الرمز هو باستخدام PDO.
الآن حان الوقت بالفعل {الاستفادة ذلك.

يمكن أن تكون المعلمات المربوطة سهلة الاستخدام

تحتاج فقط إلى واجهة برمجة تطبيقات أقل عملية.

pdo_query() يضيف دعمًا سهلًا للغاية للمعلمات المنضمة. تحويل الشفرة القديمة واضح ومباشر:

نقل المتغيرات الخاصة بك خارج سلسلة SQL.

  • قم بإضافتها كمعلمات دالة محددة بفواصل إلى pdo_query().
  • علامات استفهام المكان ? كعناصر نائبة حيث كانت المتغيرات من قبل.
  • تخلص من ' من علامات الاقتباس المفردة التي سبق أن أرفق بها قيم/متغيرات السلسلة.

ميزة تصبح أكثر وضوحا لرمز أطول.

في كثير من الأحيان ، لا يتم تقريب متغيرات السلسلة في SQL فقط ، ولكن متسلسلة مع المكالمات الفاصلة بينهما.

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title='" . pdo_real_escape_string($title) . "' OR id='".
   pdo_real_escape_string($title) . "' AND user <> '" .
   pdo_real_escape_string($root) . "' ORDER BY date")

مع تطبيق العناصر النائبة ? ، لن تضطر إلى القلق من ذلك:

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title=? OR id=? AND user<>? ORDER BY date", $title, $id, $root)

تذكر أن pdo_ * لا يزال يسمح إما أو.
لا تفلت من المتغير و قم بربطه في نفس الاستعلام.

  • يتم توفير ميزة العنصر النائب بواسطة PDO الحقيقي وراءها.
  • وبالتالي سمح أيضًا بقائمة العنصر النائب :named لاحقًا.

الأهم من ذلك أنه يمكنك تمرير المتغيرات $ _REQUEST [] بأمان وراء أي استعلام. عندما يتم تقديم حقول <form> تتوافق مع بنية قاعدة البيانات فهي بالضبط أقصر:

pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);

الكثير من البساطة. ولكن ، دعنا نعود إلى بعض النصائح والمشورات الإضافية حول إعادة الكتابة التي قد ترغب في التخلص منها mysql_ والهروب.

إصلاح أو إزالة أي oldschool sanitize() وظيفة

بمجرد تحويل كل شيء mysql_ المكالمات إلى pdo_query مع params المرتبطة ، قم بإزالة جميع المكالمات pdo_real_escape_string الزائدة.

على وجه الخصوص ، يجب إصلاح أي من وظائف sanitize أو clean أو filterThis أو clean_data كما تم الإعلان عنها بواسطة البرامج التعليمية المؤرخة في نموذج واحد أو الآخر:

function sanitize($str) {
   return trim(strip_tags(htmlentities(pdo_real_escape_string($str))));
}

معظم الأخطاء الصارخة هنا هو عدم وجود وثائق. الأهم من ذلك ، كان ترتيب التصفية بالترتيب الخاطئ تمامًا.

  • كان الترتيب الصحيح: إهمال stripslashes كمكالمة أعمق ، ثم trim ، فيما بعد strip_tags ، htmlentities لسياق الإخراج ، وأخيراً فقط _escape_string كما يجب أن يسبق تطبيقه مباشرة SQL interparsing.

  • ولكن كخطوة أولى فقط تخلص من _real_escape_stringcall.

  • قد تضطر إلى الاحتفاظ ببقية وظيفة sanitize() الآن إذا كانت قاعدة البيانات وتدفق التطبيق يتوقعان سلاسل آمنة من HTML. أضف تعليقًا بأنه لا ينطبق إلا على HTML الذي يهرب من الآن فصاعدًا.

  • يتم تفويض معالجة السلسلة/القيمة إلى شركة تنمية نفط عمان وبياناتها ذات المعاملات.

  • إذا كان هناك أي ذكر لـ stripslashes() في وظيفة التعقيم ، فقد يشير ذلك إلى مستوى إشراف أعلى.

    • كان ذلك شائعًا هناك للتراجع عن الضرر (الهروب المزدوج) من الإهمال magic_quotes . الذي هو مع ذلك أفضل ثابت مركزي ، وليس سلسلة حسب السلسلة.

    • استخدم أحد userland انعكاس النهج. ثم أزل الدالة stripslashes() في دالة sanitize.

    ملاحظة تاريخية على magic_quotes. تم إهمال هذه الميزة بحق. غالبًا ما يتم تصويرها على أنها خطأ الأمان الميزة. ولكن magic_quotes هي ميزة أمنية فاشلة بقدر فشل كرات التنس مثل مصدر التغذية. هذا ببساطة لم يكن هدفهم.

    قدم التطبيق الأصلي في PHP2/FI ذلك بشكل صريح مع فقط "سيتم نقل الاقتباسات تلقائيًا مما يسهل تمرير بيانات النموذج مباشرة إلى استعلامات msql". والجدير بالذكر أنه كان آمنًا بشكل سري للاستخدام مع mSQL ، كما هو مدعوم ASCII فقط.
    ثم أعاد PHP3/Zend عرض magic_quotes لـ MySQL وأخطأ في توثيقه. لكن في الأصل كانت مجرد ميزة ميزة راحة ، لا تنوي الأمان.

كيف تختلف البيانات المعدة

عندما تقوم بتدافع متغيرات السلسلة في استعلامات SQL ، لا يصبح الأمر أكثر تعقيدًا بالنسبة لك لمتابعة. إنه أيضًا جهد شاق لـ MySQL لفصل الشفرة والبيانات مرة أخرى.

يتم حقن SQL ببساطة عندما يكون البيانات تنزف إلى كود السياق. لا يمكن لخادم قاعدة البيانات في وقت لاحق تحديد مكان PHP المتغيرات الملصقة في الأصل بين جمل الاستعلام.

باستخدام المعلمات المرتبطة ، تقوم بفصل كود SQL وقيم سياق SQL في كود PHP. لكن لا يتم تبديلها مرة أخرى خلف الكواليس (باستثناء PDO :: EMULATE_PREPARES). تتلقى قاعدة البيانات الخاصة بك أوامر SQL غير متغير وقيم متغير 1: 1.

بينما تؤكد هذه الإجابة على أنك يجب أن تهتم بمزايا القابلية للإسقاط mysql_. هناك أحيانًا أيضًا ميزة أداء (INSERTs المتكررة ذات القيم المختلفة فقط) بسبب فصل البيانات/الرمز المرئي والتقني.

احذر أن ربط المعلمة ما زال ليس حلاً سحريًا واحدًا مقابل all حقن SQL. يتعامل مع الاستخدام الأكثر شيوعًا للبيانات/القيم. ولكن لا يمكن إدراج معرفات اسم الجدول/العمود في القائمة البيضاء أو المساعدة في إنشاء جملة ديناميكية أو قوائم قيم صفيف عادي فقط.

استخدام PDO الهجين

تجعل وظائف المجمّع pdo_* واجهة برمجة تطبيقات توقف فجوة للتشفير. (إلى حد كبير ما كان يمكن أن يكون MYSQLI لو لم يكن لتغيير توقيع الوظيفة المميزة). كما أنها تعرض PDO الحقيقي في معظم الأوقات.
لا يجب أن تتوقف إعادة الكتابة عن استخدام أسماء وظائف pdo_ الجديدة. يمكنك واحدًا تلو الآخر نقل كل pdo_query () إلى استدعاء pdo-> عادي (> -> execute ()).

من الأفضل البدء في التبسيط مرة أخرى. على سبيل المثال ، النتيجة المشتركة هي جلب:

$result = pdo_query("SELECT * FROM tbl");
while ($row = pdo_fetch_assoc($result)) {

يمكن استبداله فقط بتكرار foreach:

foreach ($result as $row) {

أو الأفضل من ذلك ، استرداد مجموعة كاملة وكاملة:

$result->fetchAll();

ستحصل على تحذيرات أكثر فائدة في معظم الحالات من PDO أو mysql_ عادة ما تقدم بعد استفسارات فاشلة.

خيارات أخرى

لذلك ، تصور هذا بعضًا من الأسباب {عملي ومسارًا يساوي قيمتها mysql_.

مجرد التبديل إلى pdo لا يقطعها تمامًا. pdo_query() هي مجرد واجهة أمامية.

ما لم تقدم أيضًا ربطًا للمعلمة أو يمكنك استخدام شيء آخر من واجهة برمجة التطبيقات (API) الرائعة ، فهذا يعد مفتاحًا لا معنى له. آمل أن يتم تصويرها بطريقة بسيطة بما يكفي لعدم زيادة الإحباط للقادمين الجدد. (التعليم عادة ما يكون أفضل من الحظر).

على الرغم من أنها مؤهلة للحصول على فئة أبسط ما يمكن أن يكون العمل ، إلا أنها لا تزال أيضًا رمز تجريبي للغاية. لقد كتبت فقط خلال عطلة نهاية الأسبوع. هناك عدد كبير من البدائل. مجرد جوجل ل PHP تجريد قاعدة البيانات وتصفح قليلا. كانت هناك دائما وستكون هناك الكثير من المكتبات الممتازة لمثل هذه المهام.

إذا كنت ترغب في تبسيط تفاعل قاعدة البيانات الخاصة بك بشكل أكبر ، فإن المصممين مثل Paris/Idiorm يستحقون المحاولة. تمامًا مثلما لم يعد أحد يستخدم دومًا لطيفًا في جافا سكريبت ، فلن تضطر إلى babysit لواجهة قاعدة بيانات خام هذه الأيام.

209
mario

وظائف mysql_:

  1. عفا عليها الزمن - لم يتم الحفاظ عليها بعد الآن
  2. لا تسمح لك بالانتقال بسهولة إلى قاعدة بيانات أخرى
  3. لا تدعم البيانات المعدة ، وبالتالي
  4. تشجيع المبرمجين على استخدام السَلسَلة لبناء الاستعلامات ، مما يؤدي إلى ثغرات حقن SQL
138
Alnitak

الحديث عن التقنية الأسباب ، لا يوجد سوى عدد قليل ، محددة للغاية ونادرا ما تستخدم. على الأرجح أنك لن تستخدمها أبدًا في حياتك.
ربما أكون جاهلًا جدًا ، لكن لم تتح لي الفرصة أبدًا لاستخدامها مثل أشياء

  • استعلامات غير محظورة وغير متزامنة
  • الإجراءات المخزنة إرجاع نتائج متعددة
  • التشفير (SSL)
  • ضغط

إذا كنت في حاجة إليها - فهذه بلا شك أسباب تقنية للابتعاد عن امتداد mysql نحو شيء أكثر أناقة وعصرية.

ومع ذلك ، هناك أيضًا بعض المشكلات غير الفنية ، والتي يمكن أن تجعل تجربتك أكثر صعوبة بعض الشيء

  • سيؤدي الاستخدام الإضافي لهذه الوظائف مع الإصدارات PHP الحديثة إلى رفع إشعارات المستوى المهملة. انهم ببساطة يمكن أن تكون مغلقة.
  • في المستقبل البعيد ، يمكن إزالتها من الإصدار الافتراضي PHP. ليست مشكلة كبيرة أيضًا ، حيث سيتم نقل mydsql ext إلى PECL وسيكون كل مضيف سعيدًا بترجمة PHP معها ، لأنهم لا يريدون أن يفقدوا عملاء كانت مواقعهم تعمل لعدة عقود.
  • مقاومة قوية من المجتمع Stackoverflow. في كل مرة تذكر فيها هذه الوظائف الصادقة ، يتم إخبارك بأنها تخضع لمحرمات صارمة.
  • كونك مستخدمًا متوسط ​​PHP ، فمن المرجح أن تكون فكرتك في استخدام هذه الوظائف عرضة للخطأ والخطأ. فقط بسبب كل هذه البرامج التعليمية والكتيبات العديدة التي تعلمك بطريقة خاطئة. ليست الوظائف نفسها - يجب أن أؤكد ذلك - ولكن الطريقة التي يتم استخدامها.

هذه المشكلة الأخيرة هي مشكلة.
ولكن ، في رأيي ، فإن الحل المقترح ليس أفضل كذلك.
يبدو لي مثالي للغاية حلما أن يتعلم جميع هؤلاء PHP المستخدمين كيفية التعامل مع استعلامات SQL بشكل صحيح في آن واحد. على الأرجح سوف يقومون فقط بتغيير mysql_ * إلى mysqli_ * ميكانيكياً ، ترك النهج كما هو . خاصة وأن mysqli يجعل استخدام البيانات المعدة لا يصدق مؤلمة ومزعجة.
ناهيك عن أن {الأصلي البيانات المعدة ليست كافية للحماية من حقن SQL ، ولا تقدم mysqli أو PDO حلاً.

لذا ، بدلاً من محاربة هذا الامتداد الصادق ، أفضل محاربة الممارسات الخاطئة وتثقيف الناس بالطرق الصحيحة.

أيضًا ، هناك بعض الأسباب الخاطئة أو غير المهمة ، مثل

  • لا يدعم الإجراءات المخزنة (كنا نستخدم mysql_query("CALL my_proc"); للأعمار)
  • لا يدعم المعاملات (كما هو مذكور أعلاه)
  • لا يدعم العبارات المتعددة (من الذي يحتاجها؟)
  • ليس تحت التطوير النشط (فماذا يؤثر على أنت بأي طريقة عملية؟)
  • يفتقر إلى واجهة OO (لإنشاء واحدة مسألة عدة ساعات)
  • لا يدعم البيانات المعدة أو استعلامات Parametrized

آخر واحد هو نقطة مثيرة للاهتمام. على الرغم من أن mysql ext لا تدعم الأصلي البيانات المعدة ، فهي غير مطلوبة من أجل السلامة. يمكننا بسهولة مزيفة البيانات المعدة باستخدام العناصر النائبة التي تمت معالجتها يدويًا (مثلما تفعل PDO):

function paraQuery()
{
    $args  = func_get_args();
    $query = array_shift($args);
    $query = str_replace("%s","'%s'",$query); 

    foreach ($args as $key => $val)
    {
        $args[$key] = mysql_real_escape_string($val);
    }

    $query  = vsprintf($query, $args);
    $result = mysql_query($query);
    if (!$result)
    {
        throw new Exception(mysql_error()." [$query]");
    }
    return $result;
}

$query  = "SELECT * FROM table where a=%s AND b LIKE %s LIMIT %d";
$result = paraQuery($query, $a, "%$b%", $limit);

voila ، كل شيء معلمات وآمنة.

لكن حسنًا ، إذا كنت لا تحب الصندوق الأحمر في الدليل ، فثمة مشكلة في الاختيار: mysqli أو PDO؟

حسنًا ، سيكون الجواب كما يلي:

  • إذا فهمت ضرورة استخدام طبقة تجريد قاعدة البيانات وتبحث عن واجهة برمجة التطبيقات لإنشاء واحدة ، mysqli يعد اختيارًا جيدًا للغاية ، لأنه يدعم بالفعل العديد من الميزات الخاصة بـ mysql.
  • إذا ، مثل الغالبية العظمى من PHP الأشخاص ، فأنت تستخدم مكالمات واجهة برمجة تطبيقات غير مباشرة في رمز التطبيق (والذي يعتبر ممارسة خاطئة في الأساس) - PDO هو الخيار الوحيد ، لأن هذا الامتداد يتظاهر بأنه لا يقتصر الأمر على واجهة برمجة التطبيقات (API) بل هو شبه DAL ، ولكنه لا يزال غير مكتمل ولكنه يقدم العديد من الميزات المهمة ، مع اثنين منهم يجعل PDO متميزًا بشكل أساسي عن mysqli:

    • بخلاف mysqli ، يمكن لشركة PDO ربط العناصر النائبة حسب القيمة ، مما يجعل الاستعلامات التي تم إنشاؤها ديناميكيًا ممكنة دون العديد من شاشات الكود الفوضوي تمامًا.
    • على عكس mysqli ، يمكن لشركة PDO دائمًا إرجاع نتيجة الاستعلام إلى صفيف معتاد بسيط ، بينما يمكن mysqli القيام بذلك فقط على عمليات تثبيت mysqlnd.

لذلك ، إذا كنت مستخدمًا متوسطًا PHP وتريد أن توفر على نفسك الكثير من الصداع عند استخدام عبارات أصلية معدة ، فإن PDO - مرة أخرى - هو الخيار الوحيد.
ومع ذلك ، فإن شركة تنمية نفط عمان ليست رصاصة فضية للغاية ولديها صعوبات.
لذا ، كتبت حلولًا لجميع المزالق الشائعة والحالات المعقدة في علامة PDO wiki

ومع ذلك ، فإن كل شخص يتحدث عن الامتدادات يفقد دائمًا 2 من الحقائق المهمة حول Mysqli و PDO:

  1. بيان معد ليس رصاصة فضية . هناك معرفات ديناميكية لا يمكن ربطها باستخدام عبارات معدة. هناك استعلامات ديناميكية مع عدد غير معروف من المعلمات مما يجعل بناء الاستعلام مهمة صعبة.

  2. لا يجب أن تظهر وظائف mysqli_ * ولا PDO في رمز التطبيق.
    يجب أن يكون هناك طبقة تجريدية بينهما ورمز التطبيق ، والتي ستؤدي كل مهمة قذرة من الربط والتكرار ومعالجة الأخطاء ، وما إلى ذلك في الداخل ، مما يجعل رمز التطبيق DRY و نظيف. خاصة بالنسبة للحالات المعقدة مثل بناء الاستعلام الديناميكي.

لذلك ، مجرد التبديل إلى PDO أو mysqli لا يكفي. على المرء استخدام ORM ، أو أداة إنشاء استعلام ، أو أي فئة تجريد قاعدة البيانات بدلاً من استدعاء وظائف API الخام في التعليمات البرمجية الخاصة بهم.
وعلى العكس - إذا كان لديك طبقة تجريدية بين رمز التطبيق الخاص بك و mysql API - لا يهم في الواقع المحرك الذي يتم استخدامه. يمكنك استخدام mysql ext حتى يتم إهمالها ثم إعادة كتابة فصل التجريد الخاص بك بسهولة إلى محرك آخر ، وجود رمز التطبيق بالكامل.

فيما يلي بعض الأمثلة استنادًا إلى safemysql class لإظهار كيف يجب أن تكون فئة التجريد هذه:

$city_ids = array(1,2,3);
$cities   = $db->getCol("SELECT name FROM cities WHERE is IN(?a)", $city_ids);

قارن هذا سطر واحد مع مقدار الكود الذي ستحتاجه مع PDO .
ثم قارن مع كمية مجنونة من الكود ستحتاج إلى بيانات Mysqli الخام المعدة. لاحظ أن معالجة الأخطاء ، والتوصيف ، واستعلام تسجيل المدمج بالفعل وتشغيلها.

$insert = array('name' => 'John', 'surname' => "O'Hara");
$db->query("INSERT INTO users SET ?u", $insert);

قارنه بإدخالات PDO المعتادة ، عندما يتم تكرار كل اسم حقل من ستة إلى عشر مرات - في جميع هذه العناصر النائبة والترابطات وتعريفات الاستعلام المحددة.

مثال آخر:

$data = $db->getAll("SELECT * FROM goods ORDER BY ?n", $_GET['order']);

بالكاد يمكنك العثور على مثال لشركة PDO للتعامل مع مثل هذه الحالة العملية.
وسيكون كلامًا كبيرًا وغالبًا ما يكون غير آمن.

لذا ، مرةً أخرى - ليس فقط الأمر الذي يجب أن يكون عليه برنامج التشغيل الخام ، بل يجب أن يكون فئة التجريد ، ليس مفيدًا فقط للحصول على أمثلة سخيفة من دليل المبتدئين ولكن لحل أي مشكلات واقعية.

103
Your Common Sense

هناك العديد من الأسباب ، ولكن ربما يكون السبب الأكثر أهمية هو أن هذه الوظائف تشجع ممارسات البرمجة غير الآمنة لأنها لا تدعم البيانات المعدة. تساعد العبارات المعدة في منع هجمات حقن SQL.

عند استخدام وظائف mysql_* ، يجب أن تتذكر تشغيل المعلمات التي يوفرها المستخدم من خلال mysql_real_escape_string(). إذا نسيت في مكان واحد فقط أو إذا كنت تهرب فقط من جزء من المدخلات ، فقد تتعرض قاعدة بياناتك للهجوم.

باستخدام عبارات معدة بـ PDO أو mysqli ستجعل الأمر أكثر صعوبة لجعل هذه الأنواع من أخطاء البرمجة.

89
Trott

لأنه (من بين أسباب أخرى) من الصعب للغاية ضمان تعقيم بيانات الإدخال. إذا كنت تستخدم استعلامات parametrized ، كما هو الحال مع PDO أو mysqli ، فيمكنك تجنب الخطر تمامًا.

على سبيل المثال ، يمكن لأي شخص استخدام "enhzflep); drop table users" كاسم مستخدم. سوف تسمح الوظائف القديمة بتنفيذ عبارات متعددة لكل استعلام ، لذلك يمكن لشيء من هذا القبيل أن يحذف جدولًا كاملاً.

إذا كان أحد لاستخدام PDO من mysqli ، فسيكون اسم المستخدم هو "enhzflep); drop table users".

انظر bobby-tables.com .

72
enhzflep

تتم كتابة هذه الإجابة لإظهار مدى تافه تخطي رمز التحقق من صحة المستخدم PHP المكتوب بشكل سيئ ، وكيف (واستخدام ما) تعمل هذه الهجمات وكيفية استبدال وظائف MySQL القديمة ببيان آمن معد - ، لماذا ينبح مستخدمو StackOverflow (من المحتمل أن يكون لديهم كثير من الممثلين) على مستخدمين جدد يسألون أسئلة لتحسين الكود الخاص بهم.

أولاً ، لا تتردد في إنشاء قاعدة بيانات mysql للاختبار (لقد قمت بالاتصال ب الإعدادية):

mysql> create table users(
    -> id int(2) primary key auto_increment,
    -> userid tinytext,
    -> pass tinytext);
Query OK, 0 rows affected (0.05 sec)

mysql> insert into users values(null, 'Fluffeh', 'mypass');
Query OK, 1 row affected (0.04 sec)

mysql> create user 'prepared'@'localhost' identified by 'example';
Query OK, 0 rows affected (0.01 sec)

mysql> grant all privileges on prep.* to 'prepared'@'localhost' with grant option;
Query OK, 0 rows affected (0.00 sec)

بعد القيام بذلك ، يمكننا الانتقال إلى رمز PHP الخاص بنا.

لنفترض أن البرنامج النصي التالي هو عملية التحقق لمشرف على موقع ويب (مبسطة ولكن تعمل إذا قمت بنسخها واستخدامها للاختبار):

<?php 

    if(!empty($_POST['user']))
    {
        $user=$_POST['user'];
    }   
    else
    {
        $user='bob';
    }
    if(!empty($_POST['pass']))
    {
        $pass=$_POST['pass'];
    }
    else
    {
        $pass='bob';
    }

    $database='prep';
    $link=mysql_connect('localhost', 'prepared', 'example');
    mysql_select_db($database) or die( "Unable to select database");

    $sql="select id, userid, pass from users where userid='$user' and pass='$pass'";
    //echo $sql."<br><br>";
    $result=mysql_query($sql);
    $isAdmin=false;
    while ($row = mysql_fetch_assoc($result)) {
        echo "My id is ".$row['id']." and my username is ".$row['userid']." and lastly, my password is ".$row['pass']."<br>";
        $isAdmin=true;
        // We have correctly matched the Username and Password
        // Lets give this person full access
    }
    if($isAdmin)
    {
        echo "The check passed. We have a verified admin!<br>";
    }
    else
    {
        echo "You could not be verified. Please try again...<br>";
    }
    mysql_close($link);

?>

<form name="exploited" method='post'>
    User: <input type='text' name='user'><br>
    Pass: <input type='text' name='pass'><br>
    <input type='submit'>
</form>

يبدو شرعي بما فيه الكفاية للوهلة الأولى.

يجب على المستخدم إدخال تسجيل الدخول وكلمة المرور ، أليس كذلك؟

رائعة ، لا تدخل في ما يلي:

user: bob
pass: somePass

ويقدمها.

الإخراج على النحو التالي:

You could not be verified. Please try again...

ممتاز! العمل كما هو متوقع ، لنجرب الآن اسم المستخدم وكلمة المرور الفعليين:

user: Fluffeh
pass: mypass

رائعة حقا! مرحبا ، الخمسات من جميع النواحي ، رمز التحقق بشكل صحيح مسؤول. انها مثالية!

حسنا ، ليس حقا. دعنا نقول أن المستخدم هو شخص صغير ذكي. دعنا نقول أن الشخص هو أنا.

أدخل ما يلي:

user: bob
pass: n' or 1=1 or 'm=m

والإخراج هو:

The check passed. We have a verified admin!

تهانينا ، لقد سمحت لي فقط بإدخال قسم مدراءك المحميين فقط مع إدخال اسم مستخدم وكلمة مرور خاطئة. على محمل الجد ، إذا كنت لا تصدقني ، فقم بإنشاء قاعدة البيانات باستخدام الكود الذي قدمته ، وقم بتشغيل هذا الرمز PHP - والذي يبدو للوهلة الأولى أنه يتحقق من اسم المستخدم وكلمة المرور بطريقة جيدة.

لذلك ، في الإجابة ، هذا IS لماذا أنت صراخ في.

لذلك ، دعونا نلقي نظرة على ما حدث من خطأ ، ولماذا دخلت للتو كهف الخفافيش المشرف فقط. أخذت تخمينًا وافترضت أنك لم تكن حريصًا على مدخلاتك وقمت ببساطة بنقلها إلى قاعدة البيانات مباشرةً. لقد قمت بإنشاء الإدخال بطريقة ستغير الاستعلام الذي كنت تقوم بتشغيله بالفعل. لذا ، ما الذي كان من المفترض أن يكون ، وماذا انتهى به الأمر؟

select id, userid, pass from users where userid='$user' and pass='$pass'

هذا هو الاستعلام ، ولكن عندما نستبدل المتغيرات بالإدخالات الفعلية التي استخدمناها ، نحصل على ما يلي:

select id, userid, pass from users where userid='bob' and pass='n' or 1=1 or 'm=m'

تعرف على كيفية إنشاء "كلمة المرور" الخاصة بي حتى يتم إغلاق العرض الأول حول كلمة المرور أولاً ، ثم إدخال مقارنة جديدة تمامًا؟ بعد ذلك فقط من أجل الأمان ، أضفت "سلسلة" أخرى حتى يتم إغلاق عرض الأسعار الفردي كما هو متوقع في الكود الذي كان لدينا في الأصل.

ومع ذلك ، لا يتعلق الأمر بالناس الذين يصرخون عليك الآن ، بل يتعلق الأمر بكيفية جعل الكود أكثر أمانًا.

حسنًا ، إذن ما الخطأ الذي حدث ، وكيف يمكننا إصلاحه؟

هذا هو هجوم حقن SQL الكلاسيكية. واحدة من أبسط لهذه المسألة. على نطاق ناقلات الهجوم ، هذا طفل صغير يهاجم دبابة - ويفوز.

لذا ، كيف نحمي قسم المشرف المقدس ونجعله لطيفًا وآمنًا؟ أول ما يجب فعله هو التوقف عن استخدام وظائف mysql_* القديمة والقديمة حقًا. أعلم أنك اتبعت برنامجًا تعليميًا وجدته على الإنترنت وهو يعمل ، لكنه قديم ، وقد عفا عليه الزمن وفي غضون بضع دقائق ، تخطيت الأمر دون أن أتحرق.

الآن ، لديك خيارات أفضل لاستخدام mysqli_ أو PDO . أنا شخصياً من كبار المعجبين بشركة تنمية نفط عمان ، لذلك سأستخدم شركة تنمية نفط عمان في بقية هذه الإجابة. هناك مؤيدون وخدع ، لكنني شخصياً أجد أن المؤيد يفوق بكثير الخداع. إنها محمولة عبر محركات قاعدة بيانات متعددة - سواء كنت تستخدم MySQL أو Oracle أو أي شيء دموي - فقط عن طريق تغيير سلسلة الاتصال ، فهي تحتوي على جميع الميزات الرائعة التي نريد استخدامها وهي جميلة ونظيفة. أنا أحب نظيفة.

الآن ، دعونا نلقي نظرة على هذا الرمز مرة أخرى ، هذه المرة مكتوبة باستخدام كائن PDO:

<?php 

    if(!empty($_POST['user']))
    {
        $user=$_POST['user'];
    }   
    else
    {
        $user='bob';
    }
    if(!empty($_POST['pass']))
    {
        $pass=$_POST['pass'];
    }
    else
    {
        $pass='bob';
    }
    $isAdmin=false;

    $database='prep';
    $pdo=new PDO ('mysql:Host=localhost;dbname=prep', 'prepared', 'example');
    $sql="select id, userid, pass from users where userid=:user and pass=:password";
    $myPDO = $pdo->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
    if($myPDO->execute(array(':user' => $user, ':password' => $pass)))
    {
        while($row=$myPDO->fetch(PDO::FETCH_ASSOC))
        {
            echo "My id is ".$row['id']." and my username is ".$row['userid']." and lastly, my password is ".$row['pass']."<br>";
            $isAdmin=true;
            // We have correctly matched the Username and Password
            // Lets give this person full access
        }
    }

    if($isAdmin)
    {
        echo "The check passed. We have a verified admin!<br>";
    }
    else
    {
        echo "You could not be verified. Please try again...<br>";
    }

?>

<form name="exploited" method='post'>
    User: <input type='text' name='user'><br>
    Pass: <input type='text' name='pass'><br>
    <input type='submit'>
</form>

الاختلافات الرئيسية هي أنه لا توجد وظائف mysql_* أخرى. كل ذلك يتم عبر كائن PDO ، ثانياً ، يستخدم عبارة مُعدة. الآن ، ما هو بيان prepred تسأل؟ إنها طريقة لإخبار قاعدة البيانات قبل تشغيل الاستعلام ، ما هو الاستعلام الذي سنقوم بتشغيله. في هذه الحالة ، نقول لقاعدة البيانات: "مرحبًا ، سأقوم بتشغيل عبارة تحديد تريد معرفًا ، معرف مستخدم ، وتمرير من مستخدمي الجدول حيث يكون معرف المستخدم متغيرًا ، والممر هو أيضًا متغير.".

بعد ذلك ، في بيان التنفيذ ، نمرر صفيف قاعدة البيانات بكل المتغيرات التي تتوقعها الآن.

النتائج رائعة. لنجرب تركيبات اسم المستخدم وكلمة المرور هذه من قبل مرة أخرى:

user: bob
pass: somePass

لم يتم التحقق من المستخدم. رائع.

كيف حول:

user: Fluffeh
pass: mypass

لقد نجحت قليلاً: نجح الشيك. لدينا مشرف تم التحقق منه!

الآن ، دعونا نجرب البيانات التي سيدخلها الفصل الذكي لمحاولة تجاوز نظام التحقق القليل لدينا:

user: bob
pass: n' or 1=1 or 'm=m

هذه المرة ، نحصل على ما يلي:

You could not be verified. Please try again...

هذا هو السبب وراء صراخك عند نشر الأسئلة - فذلك لأن الأشخاص يمكنهم أن يروا أنه يمكن تجاوز الشفرة الخاصة بك حتى دون المحاولة. يرجى استخدام هذا السؤال والإجابة عليه لتحسين الكود ، وجعله أكثر أمانًا واستخدام الوظائف الحالية.

أخيرًا ، هذا لا يعني أن هذا رمز مثالي. هناك العديد من الأشياء التي يمكنك القيام بها لتحسينها ، واستخدام كلمات مرور مجزأة على سبيل المثال ، تأكد من أنه عندما تقوم بتخزين معلومات حساسة في قاعدة البيانات ، فإنك لا تخزنها في نص عادي ، ولها مستويات متعددة من التحقق - ولكن في الحقيقة ، إذا قمت فقط بتغيير الشفرة القديمة المعرضة للحقن الخاصة بك إلى هذا ، وسوف تكون على ما يرام لكتابة رمز جيد - وحقيقة أنك قد وصلت إلى هذا الحد وما زلت تقرأه ، تعطيني شعوراً بالأمل في أنك لن تنفذ هذا النوع فقط من التعليمات البرمجية عند كتابة مواقع الويب والتطبيقات الخاصة بك ، ولكن يمكنك الخروج والبحث في تلك الأشياء الأخرى التي ذكرتها للتو - وأكثر من ذلك. اكتب أفضل رمز يمكنك ، وليس الرمز الأساسي الذي بالكاد يعمل.

62
Fluffeh

ملحق MySQL هو الأقدم في الثلاثة وكان الطريقة الأصلية التي استخدمها المطورون للتواصل مع MySQL. هذا الامتداد الآن تم إهماله لصالح الآخر اثنينالبدائل بسبب التحسينات التي تم إجراؤها في الإصدارات الأحدث لكل من PHP و MySQL.

  • MySQLi هو الملحق "المحسن" للعمل مع قواعد بيانات MySQL. يستفيد من الميزات المتوفرة في الإصدارات الأحدث من خادم MySQL ، ويعرض كلاً من واجهة موجهة نحو الوظيفة وواجهة موجهة للكائن للمطور ويفعل بعض الأشياء الأخرى الرائعة.

  • PDO يقدم واجهة برمجة تطبيقات تعمل على دمج معظم الوظائف التي كانت موزعة مسبقًا عبر ملحقات الوصول إلى قاعدة البيانات الرئيسية ، مثل MySQL ، و PostgreSQL ، و SQLite ، و MSSQL ، وما إلى ذلك. تعرض الواجهة كائنات عالية المستوى للمبرمج للعمل. مع اتصالات قاعدة البيانات والاستعلامات ومجموعات النتائج ، وبرامج التشغيل ذات المستوى المنخفض تقوم بإجراء الاتصالات ومعالجة الموارد مع خادم قاعدة البيانات. يجري الكثير من النقاش والعمل في شركة تنمية نفط عمان وتعتبر الطريقة المناسبة للعمل مع قواعد البيانات في الكود المهني الحديث.

30
Alexander

أجد الإجابات المذكورة أعلاه مطولة حقًا ، وذلك لتلخيص:

ملحق mysqli له عدد من الفوائد ، التحسينات الرئيسية على امتداد mysql هي:

  • وجوه المنحى واجهة
  • دعم البيانات المعدة
  • دعم لبيانات متعددة
  • دعم المعاملات
  • تعزيز قدرات التصحيح
  • دعم خادم مضمن

المصدر: نظرة عامة على MySQLi


كما هو موضح في الإجابات أعلاه ، فإن بدائل mysql هي mysqli و PDO (كائنات بيانات PHP).

  • تدعم API البيانات المعدة من جانب الخادم: مدعومة بواسطة MYSQLi و PDO
  • تدعم واجهة برمجة التطبيقات (API) البيانات المعدة من جانب العميل: تدعمها شركة تنمية نفط عمان فقط
  • تدعم API الإجراءات المخزنة: كلا MySQLi و PDO
  • تدعم API بيانات متعددة وجميع وظائف MySQL 4.1+ - مدعومة من MySQLi وأيضًا بواسطة PDO

تم تقديم كل من MySQLi و PDO في PHP 5.0 ، بينما تم تقديم MySQL قبل PHP 3.0. وتجدر الإشارة إلى أن MySQL مدرج في PHP5.x رغم أنه تم إهماله في الإصدارات الأحدث.

19
Ani Menon

من الممكن تحديد جميع وظائف mysql_* تقريبًا باستخدام mysqli أو PDO. ما عليك سوى تضمينها أعلى تطبيق PHPالقديم الخاص بك ، وسيعمل على PHP7. حل بلدي هنا .

<?php

define('MYSQL_LINK', 'dbl');
$GLOBALS[MYSQL_LINK] = null;

function mysql_link($link=null) {
    return ($link === null) ? $GLOBALS[MYSQL_LINK] : $link;
}

function mysql_connect($Host, $user, $pass) {
    $GLOBALS[MYSQL_LINK] = mysqli_connect($Host, $user, $pass);
    return $GLOBALS[MYSQL_LINK];
}

function mysql_pconnect($Host, $user, $pass) {
    return mysql_connect($Host, $user, $pass);
}

function mysql_select_db($db, $link=null) {
    $link = mysql_link($link);
    return mysqli_select_db($link, $db);
}

function mysql_close($link=null) {
    $link = mysql_link($link);
    return mysqli_close($link);
}

function mysql_error($link=null) {
    $link = mysql_link($link);
    return mysqli_error($link);
}

function mysql_errno($link=null) {
    $link = mysql_link($link);
    return mysqli_errno($link);
}

function mysql_ping($link=null) {
    $link = mysql_link($link);
    return mysqli_ping($link);
}

function mysql_stat($link=null) {
    $link = mysql_link($link);
    return mysqli_stat($link);
}

function mysql_affected_rows($link=null) {
    $link = mysql_link($link);
    return mysqli_affected_rows($link);
}

function mysql_client_encoding($link=null) {
    $link = mysql_link($link);
    return mysqli_character_set_name($link);
}

function mysql_thread_id($link=null) {
    $link = mysql_link($link);
    return mysqli_thread_id($link);
}

function mysql_escape_string($string) {
    return mysql_real_escape_string($string);
}

function mysql_real_escape_string($string, $link=null) {
    $link = mysql_link($link);
    return mysqli_real_escape_string($link, $string);
}

function mysql_query($sql, $link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, $sql);
}

function mysql_unbuffered_query($sql, $link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, $sql, MYSQLI_USE_RESULT);
}

function mysql_set_charset($charset, $link=null){
    $link = mysql_link($link);
    return mysqli_set_charset($link, $charset);
}

function mysql_get_Host_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_Host_info($link);
}

function mysql_get_proto_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_proto_info($link);
}
function mysql_get_server_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_server_info($link);
}

function mysql_info($link=null) {
    $link = mysql_link($link);
    return mysqli_info($link);
}

function mysql_get_client_info() {
    $link = mysql_link();
    return mysqli_get_client_info($link);
}

function mysql_create_db($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "CREATE DATABASE `$db`");
}

function mysql_drop_db($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "DROP DATABASE `$db`");
}

function mysql_list_dbs($link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, "SHOW DATABASES");
}

function mysql_list_fields($db, $table, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    $table = str_replace('`', '', mysqli_real_escape_string($link, $table));
    return mysqli_query($link, "SHOW COLUMNS FROM `$db`.`$table`");
}

function mysql_list_tables($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "SHOW TABLES FROM `$db`");
}

function mysql_db_query($db, $sql, $link=null) {
    $link = mysql_link($link);
    mysqli_select_db($link, $db);
    return mysqli_query($link, $sql);
}

function mysql_fetch_row($qlink) {
    return mysqli_fetch_row($qlink);
}

function mysql_fetch_assoc($qlink) {
    return mysqli_fetch_assoc($qlink);
}

function mysql_fetch_array($qlink, $result=MYSQLI_BOTH) {
    return mysqli_fetch_array($qlink, $result);
}

function mysql_fetch_lengths($qlink) {
    return mysqli_fetch_lengths($qlink);
}

function mysql_insert_id($qlink) {
    return mysqli_insert_id($qlink);
}

function mysql_num_rows($qlink) {
    return mysqli_num_rows($qlink);
}

function mysql_num_fields($qlink) {
    return mysqli_num_fields($qlink);
}

function mysql_data_seek($qlink, $row) {
    return mysqli_data_seek($qlink, $row);
}

function mysql_field_seek($qlink, $offset) {
    return mysqli_field_seek($qlink, $offset);
}

function mysql_fetch_object($qlink, $class="stdClass", array $params=null) {
    return ($params === null)
        ? mysqli_fetch_object($qlink, $class)
        : mysqli_fetch_object($qlink, $class, $params);
}

function mysql_db_name($qlink, $row, $field='Database') {
    mysqli_data_seek($qlink, $row);
    $db = mysqli_fetch_assoc($qlink);
    return $db[$field];
}

function mysql_fetch_field($qlink, $offset=null) {
    if ($offset !== null)
        mysqli_field_seek($qlink, $offset);
    return mysqli_fetch_field($qlink);
}

function mysql_result($qlink, $offset, $field=0) {
    if ($offset !== null)
        mysqli_field_seek($qlink, $offset);
    $row = mysqli_fetch_array($qlink);
    return (!is_array($row) || !isset($row[$field]))
        ? false
        : $row[$field];
}

function mysql_field_len($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    return is_object($field) ? $field->length : false;
}

function mysql_field_name($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    if (!is_object($field))
        return false;
    return empty($field->orgname) ? $field->name : $field->orgname;
}

function mysql_field_table($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    if (!is_object($field))
        return false;
    return empty($field->orgtable) ? $field->table : $field->orgtable;
}

function mysql_field_type($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    return is_object($field) ? $field->type : false;
}

function mysql_free_result($qlink) {
    try {
        mysqli_free_result($qlink);
    } catch (Exception $e) {
        return false;
    }
    return true;
}
2
Pavel Tzonkov

الدالات التي تشبه هذا النوع mysql_connect() ، mysql_query() هي الإصدار السابق PHP أي (PHP 4) الدالتين والآن ليست قيد الاستخدام.

يتم استبدال هذه بـ mysqli_connect() ، mysqli_query() بشكل مشابه في أحدث PHP5.

هذا هو السبب وراء الخطأ.

0
Killer

هذا سؤال قديم اليوم (يناير 2019) ولكن هذا قد لا يزال مفيدًا. منذ حوالي 7 سنوات ، قمت بإنشاء تخطيط جدولي لوظيفة MySQL/MySQLi/PDO. قد يكون مرجعا مفيدا. انها على الانترنت هنا و مستنسخة أدناه. لا تتردد في نسخ ولصق HTML.

وكمسألة عملية ، وجدت أن تحويل وظائف MySQL الإجرائية إلى OOP MySQLi كان المسار الأقل مقاومة. من الجيد تمامًا أن يتم فتح اتصالين DB في نفس الوقت ، وقد منحنا ذلك بعض المرونة أثناء عملنا على التحويلات - يمكننا تحويل البرامج النصية على أساس تدريجي ، استعلام واحد في المرة الواحدة. على الرغم من أنني قد لا أوصي بذلك اليوم ، إلا أنه كان مناسبًا في ذلك الوقت.

<div class="container">

<h2>Mapping Obsolete MySQL Functions to Current PHP Extensions</h2>
<table>
<tr><th>MySQL Extension</th><th>MySQL<b><i>i</i></b></th><th>PDO</th></tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-affected-rows.php">mysql_affected_rows</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.affected-rows.php">mysqli::$affected_rows</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.rowcount.php">PDOStatement::rowCount</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-client-encoding.php">mysql_client_encoding</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.character-set-name.php">mysqli::character_set_name</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-close.php">mysql_close</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.close.php">mysqli::close</a></td>
    <td>Assign NULL to PDO Object</td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-connect.php">mysql_connect</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.construct.php">mysqli::__construct</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.construct.php">PDO::__construct</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-create-db.php">mysql_create_db</a></td>
    <td>Query: CREATE DATABASE</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-data-seek.php">mysql_data_seek</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-stmt.data-seek.php">mysqli_stmt::data_seek</a></td>
    <td>PDO::FETCH_ORI_ABS (?)</td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-db-name.php">mysql_db_name</a></td>
    <td>Query: SELECT DATABASE()</td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-db-query.php">mysql_db_query</a></td>
    <td> </td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-drop-db.php">mysql_drop_db</a></td>
    <td>Query: DROP DATABASE</td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-errno.php">mysql_errno</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.errno.php">mysqli::$errno</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.errorcode.php">PDO::errorCode</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-error.php">mysql_error</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.error-list.php">mysqli::$error_list</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.errorinfo.php">PDO::errorInfo</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-escape-string.php">mysql_escape_string</a></td>
    <td> </td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-fetch-array.php">mysql_fetch_array</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-array.php">mysqli_result::fetch_array</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.fetch.php">PDOStatement::fetch</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-fetch-assoc.php">mysql_fetch_assoc</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-assoc.php">mysqli_result::fetch_assoc</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.fetch.php">PDOStatement::fetch</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-fetch-field.php">mysql_fetch_field</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-field.php">mysqli_result::fetch_field</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-fetch-lengths.php">mysql_fetch_lengths</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.lengths.php">mysqli_result::$lengths</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-fetch-object.php">mysql_fetch_object</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-object.php">mysqli_result::fetch_object</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.fetch.php">PDOStatement::fetch</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-fetch-row.php">mysql_fetch_row</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-row.php">mysqli_result::fetch_row</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.fetch.php">PDOStatement::fetch</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-field-flags.php">mysql_field_flags</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-fields.php">mysqli_result::fetch_fields</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-field-len.php">mysql_field_len</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-field-direct.php">mysqli_result::fetch_field_direct</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-field-name.php">mysql_field_name</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-field-direct.php">mysqli_result::fetch_field_direct</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-field-seek.php">mysql_field_seek</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.field-seek.php">mysqli_result::field_seek</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.fetch.php">PDOStatement::fetch</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-field-table.php">mysql_field_table</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-field-direct.php">mysqli_result::fetch_field_direct</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-field-type.php">mysql_field_type</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.fetch-field-direct.php">mysqli_result::fetch_field_direct</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.getcolumnmeta.php">PDOStatement::getColumnMeta</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-free-result.php">mysql_free_result</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-result.free.php">mysqli_result::free</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.closecursor.php">PDOStatement::closeCursor</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-get-client-info.php">mysql_get_client_info</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.get-client-info.php">mysqli::get_client_info</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.getattribute.php">PDO::getAttribute</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-get-Host-info.php">mysql_get_Host_info</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.get-Host-info.php">mysqli::$Host_info</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.getattribute.php">PDO::getAttribute</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-get-proto-info.php">mysql_get_proto_info</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.get-proto-info.php">mysqli::$protocol_version</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-get-server-info.php">mysql_get_server_info</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.get-server-info.php">mysqli::$server_info</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.getattribute.php">PDO::getAttribute</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-info.php">mysql_info</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.info.php">mysqli::$info</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-insert-id.php">mysql_insert_id</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.insert-id.php">mysqli::$insert_id</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.lastinsertid.php">PDO::lastInsertId</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-list-dbs.php">mysql_list_dbs</a></td>
    <td>Query: SHOW DATABASES</td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-list-fields.php">mysql_list_fields</a></td>
    <td>Query: SHOW COLUMNS</td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-list-processes.php">mysql_list_processes</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.thread-id.php">mysqli::$thread_id</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-list-tables.php">mysql_list_tables</a></td>
    <td>Query: SHOW TABLES</td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-num-fields.php">mysql_num_fields</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.field-count.php">mysqli::$field_count</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.columncount.php">PDOStatement::columnCount</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-num-rows.php">mysql_num_rows</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli-stmt.num-rows.php">mysqli_stmt::$num_rows</a></td>
    <td><a href="http://www.php.net/manual/en/pdostatement.rowcount.php">PDOStatement::rowCount</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-pconnect.php">mysql_pconnect</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.construct.php">mysqli::__construct</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.construct.php">PDO::__construct</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-ping.php">mysql_ping</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.ping.php">mysqli::ping</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-query.php">mysql_query</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.query.php">mysqli::query</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.query.php">PDO::query</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-real-escape-string.php">mysql_real_escape_string</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.real-escape-string.php">mysqli::real_escape_string</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.quote.php">PDO::quote</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-result.php">mysql_result</a></td>
    <td>Combination</td>
    <td><a href="http://www.php.net/manual/en/pdostatement.fetchcolumn.php">PDOStatement::fetchColumn</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-select-db.php">mysql_select_db</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.send-query.php">mysqli::send_query</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.construct.php">PDO::__construct</a></td></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-set-charset.php">mysql_set_charset</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.character-set-name.php">mysqli::character_set_name</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.construct.php">PDO::__construct</a></td></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-stat.php">mysql_stat</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.stat.php">mysqli::stat</a></td>
    <td><a href="http://www.php.net/manual/en/pdo.getattribute.php">PDO::getAttribute</a></td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-tablename.php">mysql_tablename</a></td>
    <td>Query: SHOW TABLES</td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-thread-id.php">mysql_thread_id</a></td>
    <td><a href="http://www.php.net/manual/en/mysqli.thread-id.php">mysqli::$thread_id</a></td>
    <td> </td>
    </tr>
<tr><td><a href="http://www.php.net/manual/en/function.mysql-unbuffered-query.php">mysql_unbuffered_query</a></td>
    <td>See <a href="http://www.php.net/manual/en/mysqlinfo.concepts.buffering.php">Buffering Concepts</a></td>
    <td> </td>
    </tr>
</table>

</div><!-- container -->
0
Ray Paseur