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


Answers

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

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

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

تم deprecated ext/mysql deprecated وتمت إزالته بدءًا من PHP 7 .

انظر الصندوق الأحمر؟

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

لماذا ا

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

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

سبب عدم استخدام وظيفة mysql_* :

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

فوق النقطة المقتبسة من جواب كوينتن

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

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

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

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

error_reporting = E_ALL ^ E_DEPRECATED

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

المادة PDO مقابل MySQLi: التي يجب عليك استخدامها؟ من جانب ديان ماريانوفيتش سوف تساعدك على الاختيار.

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

برنامج تعليمي بسيط وقصير للشركة

س: كان السؤال الأول في ذهني هو: ما هو "PDO"؟

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

الاتصال ب MySQL

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

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

مع PDO : كل ​​ما عليك القيام به هو إنشاء كائن PDO جديد. يقبل المُنشئ معلمات لتحديد مصدر قاعدة البيانات ، حيث يأخذ مُنشئ الشركة في الغالب أربعة معلمات هي DSN (اسم مصدر البيانات) واسم username اختياريًا 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));

هل يمكننا تحديد السمات بعد بناء الشركة؟

نعم ، يمكننا أيضًا تعيين بعض السمات بعد إنشاء الشركة باستخدام طريقة 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 . يمكننا فرض PDO في أحد أوضاع الخطأ الثلاثة عن طريق تعيين سمة وضع الخطأ. توجد ثلاثة أوضاع للتعامل مع الأخطاء أدناه.

  • PDO::ERRMODE_SILENT . انها مجرد تعيين رموز الخطأ وتعمل إلى حد كبير مثل mysql_* حيث يجب عليك التحقق من كل نتيجة ثم ننظر في $db->errorInfo(); للحصول على تفاصيل الخطأ.
  • PDO::ERRMODE_WARNING رفع E_WARNING . (تحذيرات وقت التشغيل (أخطاء غير فادحة). لا يتم إيقاف تنفيذ البرنامج النصي.)
  • PDO::ERRMODE_EXCEPTION : 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 - الآن. يمكنك الإمساك بها في أي وقت مناسب ، لكنني أوصي بشدة باستخدام try . قد يكون من المنطقي أيضًا الإمساك بها خارج الوظيفة التي تستدعي 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 ؟ لا تقلق ، هنا نذهب:

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

إذن ما تفعله في 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 في PDO::FETCH_ASSOC fetch() fetchAll() أعلاه. هذا يخبر PDO بإرجاع الصفوف كصفيف اقتران بأسماء الحقول كمفاتيح. هناك العديد من طرق الجلب الأخرى التي سأشرحها واحدًا تلو الآخر.

أولاً ، أشرح كيفية تحديد طريقة الجلب:

 $stmt->fetch(PDO::FETCH_ASSOC)

في ما سبق ، لقد تم استخدام fetch() . تستطيع ايضا استخذام:

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

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

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

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

بدلاً من استخدام 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();

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

ما نقوم به في وظيفة 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 وإرجاع عدد الصفوف المتأثرة.

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

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

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

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

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

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

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

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

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

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

س. إذن ، ما الذي يسمى بالأسماء المؤقتة وكيف أستخدمها؟
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);

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

<?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); النائبة المختلفة ( $stmt->bindValue(1, $name, PDO::PARAM_STR); ). بعد ذلك ، نقوم بتعيين قيم لتلك العناصر النائبة وتنفيذ البيان. في المثال الثاني ، ينتقل العنصر الصفيف الأول إلى الأول ? والثانية إلى الثانية ? .

ملاحظة : في العناصر النائبة غير المسماة ، يجب علينا الاهتمام بالترتيب الصحيح للعناصر في المصفوفة التي PDOStatement::execute() إلى طريقة 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 /*"));
Question

ما هي الأسباب الفنية لماذا لا يجب استخدام وظائف mysql_* ؟ (مثل mysql_query() أو mysql_connect() أو mysql_real_escape_string()

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

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

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




It's possible to define almost all mysql_* functions using mysqli or PDO. Just include them on top of your old PHP application, and it will work on PHP7. My solution here .

<?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;
}



mysql_* functions were depreciated (as of php 5.5 ) given the fact that better functions and code structures were developed. The fact that the function was depreciated means that no more effort will be placed into improving it in terms of performance and security, which means it is less future proof .

If you need more reasons:

  • mysql_* functions do not support prepared statements.
  • mysql_* functions do not support the binding of parameters.
  • mysql_* functions lack functionality for Object Oriented Programming.
  • the list goes on ...



Because (amongst other reasons) it's much harder to ensure the input data is sanitized. If you use parametrized queries, as one does with PDO or mysqli you can entirely avoid the risk.

As an example, someone could use "enhzflep); drop table users" as a username. The old functions will allow executing multiple statements per query, so something like that nasty bugger can delete a whole table.

If one were to use PDO of mysqli, the user-name would end-up being "enhzflep); drop table users" .

See bobby-tables.com .




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

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

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

إنها في الغالب المعلمات المرتبطة التي يمكن أن تبسط التعليمات البرمجية. ومع share فإن الانتقال إلى الشركة ليس أمرًا شاقًا للغاية.

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

وظائف pdo_ * المكافئة بدلاً من mysql_ *

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

  1. ببساطة include_once( <pdo_mysql.php> ); في كل نص برمجي استفزازي يتفاعل مع قاعدة البيانات.

  2. قم بإزالة بادئة وظيفة mysql_ كل مكان pdo_ بـ 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]";
    }
    

Et voilà.
كودك يستخدم PDO.
الآن حان الوقت لاستخدامها بالفعل.

يمكن أن تكون المعلمات المقيدة سهلة الاستخدام

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

يضيف pdo_query() دعم سهل جدًا للمعلمات المربوطة. تحويل رمز قديم بسيط:

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

  • pdo_query() كمعلمات الدالة الفاصلة محددة إلى 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_ والهروب.

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

بمجرد أن تقوم بتحويل كافة المكالمات mysql_ إلى pdo_query مع params المنضمة ، قم بإزالة كافة مكالمات pdo_real_escape_string .

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

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

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

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

  • ولكن كخطوة أولى فقط تخلص من استدعاء _real_escape_string .

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

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

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

    ملاحظة تاريخية على 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 بقيم مختلفة فقط) بسبب فصل البيانات / الشفرة المرئي والفني.

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

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

pdo_* هذه pdo_* واجهة برمجة تطبيقات لإيقاف التشغيل. (إلى حد كبير ما MYSQLI يمكن أن يكون MYSQLI لو لم يكن MYSQLI التوقيع الدالة MYSQLI ). كما أنهم يعرضون PDO الحقيقية في معظم الأوقات.
إعادة الكتابة لا يجب أن تتوقف عند استخدام أسماء الدوال pdo_ الجديدة. يمكنك تحويل واحد كل واحد pdo_query () إلى pd $ عادي -> تحضير () -> تنفيذ () المكالمة.

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

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

يمكن الاستعاضة عنها بتكرار foreach:

foreach ($result as $row) {

أو الأفضل بعد استرجاع مجموعة مباشرة وكاملة:

$result->fetchAll();

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

خيارات أخرى

لذلك هذا الأمل تصور بعض الأسباب العملية ومسار يستحق أن يسقط mysql_ .

مجرد التبديل إلى pdo لا pdo تماما. pdo_query() هو أيضا مجرد واجهة على ذلك.

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

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

إذا كنت تريد تبسيط تفاعل قاعدة البيانات بشكل أكبر ، Paris/Idiorm تجربة مصمم الخرائط مثل Paris/Idiorm . Just like nobody uses the bland DOM in JavaScript anymore, you don't have to babysit a raw database interface nowadays.




Speaking of technical reasons, there are only a few, extremely specific and rarely used. Most likely you will never ever use them in your life.
Maybe I am too ignorant, but I never had an opportunity to use them things like

  • non-blocking, asynchronous queries
  • stored procedures returning multiple resultsets
  • Encryption (SSL)
  • Compression

If you need them - these are no doubt technical reasons to move away from mysql extension toward something more stylish and modern-looking.

Nevertheless, there are also some non-technical issues, which can make your experience a bit harder

  • further use of these functions with modern PHP versions will raise deprecated-level notices. They simply can be turned off.
  • in a distant future, they can be possibly removed from the default PHP build. Not a big deal too, as mydsql ext will be moved into PECL and every hoster will be happy to compile PHP with it, as they don't want to lose clients whose sites were working for decades.
  • strong resistance from community. Еverytime you mention these honest functions, you being told that they are under strict taboo.
  • being an average PHP user, most likely your idea of using these functions is error-prone and wrong. Just because of all these numerous tutorials and manuals which teach you the wrong way. Not the functions themselves - I have to emphasize it - but the way they are used.

This latter issue is a problem.
But, in my opinion, the proposed solution is no better either.
It seems to me too idealistic a dream that all those PHP users will learn how to handle SQL queries properly at once. Most likely they would just change mysql_* to mysqli_* mechanically, leaving the approach the same . Especially because mysqli makes prepared statements usage incredible painful and troublesome.
Not to mention that native prepared statements aren't enough to protect from SQL injections, and neither mysqli nor PDO offers a solution.

So, instead of fighting this honest extension, I'd prefer to fight wrong practices and educate people in the right ways.

Also, there are some false or non-significant reasons, like

  • Doesn't support Stored Procedures (we were using mysql_query("CALL my_proc"); for ages)
  • Doesn't support Transactions (same as above)
  • Doesn't support Multiple Statements (who need them?)
  • Not under active development (so what? does it affect you in any practical way?)
  • Lacks an OO interface (to create one is a matter of several hours)
  • Doesn't support Prepared Statements or Parametrized Queries

The last one is an interesting point. Although mysql ext do not support native prepared statements, they aren't required for the safety. We can easily fake prepared statements using manually handled placeholders (just like PDO does):

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 , everything is parameterized and safe.

But okay, if you don't like the red box in the manual, a problem of choice arises: mysqli or PDO?

Well, the answer would be as follows:

  • If you understand the necessity of using a database abstraction layer and looking for an API to create one, mysqli is a very good choice, as it indeed supports many mysql-specific features.
  • If, like vast majority of PHP folks, you are using raw API calls right in the application code (which is essentially wrong practice) - PDO is the only choice , as this extension pretends to be not just API but rather a semi-DAL, still incomplete but offers many important features, with two of them makes PDO critically distinguished from mysqli:

    • unlike mysqli, PDO can bind placeholders by value , which makes dynamically built queries feasible without several screens of quite messy code.
    • unlike mysqli, PDO can always return query result in a simple usual array, while mysqli can do it only on mysqlnd installations.

So, if you are an average PHP user and want to save yourself a ton of headaches when using native prepared statements, PDO - again - is the only choice.
However, PDO is not a silver bullet too and has its hardships.
So, I wrote solutions for all the common pitfalls and complex cases in the PDO tag wiki

Nevertheless, everyone talking about extensions always missing the 2 important facts about Mysqli and PDO:

  1. Prepared statement isn't a silver bullet . There are dynamical identifiers which cannot be bound using prepared statements. There are dynamical queries with an unknown number of parameters which makes query building a difficult task.

  2. Neither mysqli_* nor PDO functions should have appeared in the application code.
    There ought to be an abstraction layer between them and application code, which will do all the dirty job of binding, looping, error handling, etc. inside, making application code DRY and clean. Especially for the complex cases like dynamical query building.

So, just switching to PDO or mysqli is not enough. One has to use an ORM, or a query builder, or whatever database abstraction class instead of calling raw API functions in their code.
And contrary - if you have an abstraction layer between your application code and mysql API - it doesn't actually matter which engine is used. You can use mysql ext until it goes deprecated and then easily rewrite your abstraction class to another engine, having all the application code intact.

Here are some examples based on my safemysql class to show how such an abstraction class ought to be:

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

Compare this one single line with amount of code you will need with PDO .
Then compare with crazy amount of code you will need with raw Mysqli prepared statements. Note that error handling, profiling, query logging already built in and running.

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

Compare it with usual PDO inserts, when every single field name being repeated six to ten times - in all these numerous named placeholders, bindings, and query definitions.

مثال آخر:

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

You can hardly find an example for PDO to handle such practical case.
And it will be too wordy and most likely unsafe.

So, once more - it is not just raw driver should be your concern but abstraction class, useful not only for silly examples from beginner's manual but to solve whatever real-life problems.




The MySQL extension is the oldest of the three and was the original way that developers used to communicate with MySQL. This extension is now being deprecated in favor of the other mysqli_ PDO because of improvements made in newer releases of both PHP and MySQL.

  • mysqli_ is the 'improved' extension for working with MySQL databases. It takes advantage of features that are available in newer versions of the MySQL server, exposes both a function-oriented and an object-oriented interface to the developer and a does few other nifty things.

  • PDO offers an API that consolidates most of the functionality that was previously spread across the major database access extensions, ie MySQL, PostgreSQL, SQLite, MSSQL, etc. The interface exposes high-level objects for the programmer to work with database connections, queries and result sets, and low-level drivers perform communication and resource handling with the database server. A lot of discussion and work is going into PDO and it's considered the appropriate method of working with databases in modern, professional code.




Related