الرئيسية > برمجة > مقدمة إلى PDO

مقدمة إلى PDO


السلام عليكم و رحمة الله و بركاته
سيكون درس اليوم إن شاء الله عن إستخدام PDO.
محتويات الدرس:
مقدمة
لماذا PDO
مميزات PDO
القواعد البيانات المدعومة
تنصيب PDO
الإتصال بقاعدة البيانات
معالجة الإستثناءات
تحديد الخيارات لإتصال
إستخدام bindValue و تحضير الإستعلام
تنفيذ الإستعلام بإستخدام execute
الفرق بين execute، exec و query
إستخدام fetch، fetchAll
قطع الإتصال بقاعدة البيانات

مقدمة: ماهو معروف في عالم قواعد البيانات أن MySQL هو الرائج في هذا المجال مما جعل أغلب المبرمجين يتبنونه في تطبيقاتهم، حتى أنك لو فتحت مصدر سكريبت ما سترى mysql_* منتشرة في كل مكان، ماذا لو كان من سيستخدم السكربت يستعمل محرك قواعد بيانات غير MySQL، هذا بالتأكيد سيتطلب إعادة كتابة أجزاء كبيرة من السكريبت، هنا ظهرت بعض الكائنات بال PHP تدعم أكثر من محرك قواعد بيانات و توفر واجهة دوال محمولة، يمكنك إستخدامها مع أي محرك مدعوم من طرف الكائن، كمثال على ذلك ADOdb و PDO.
لماذا PDO:
PDO أصبحت الكائن أكثر إستعمالا حاليا لأنها الأحدث، و دعم لقواعد بيانات كبيرة مع كثرة الإختيارات، لا ننسى قابيلة إضافة قواعد من طرف المستخدم في حال كنت مطور.
مميزات PDO:
_ دعم لعدد قواعد بيانات كبيرة.
_ إستخدام OOP.
_ قابلية الإضافة مما يتيح دعم نوع جديد من القواعد البيانات.
_ نفس الدوال تقريبا لجميع قواعد البيانات، مما يكسب سكربتك تعدد القواعد البيانات(cross databases).
_ تعدد الخيارات على حسب قاعدة البيانات.
_ حماية من SQL injection إلى درجة ما.
القواعد البيانات المدعومة:
حاليا القواعد المدعومة هي:
FreeTDS / Microsoft SQL Server / Sybase
Firebird/Interbase 6
IBM DB2
IBM Informix Dynamic Server
MySQL 3.x/4.x/5.x
Oracle Call Interface
ODBC v3 (IBM DB2, unixODBC and win32 ODBC)
PostgreSQL
4D
SQLite 3 and SQLite 2
تنصيب PDO:
بالنسبة لأنظمة unix-like فإن تنصيبها يأتي عند تنصيب PHP كالآتي:

tar -xjf php-5.3.4.tar.bz2
cd php-5.3.4
./configure --with-pdo-mysql=shared –with-pdo-pgsql=shared

في حالتي هذه قمت بدعم MySQL و PostgreSQL.
بالنسبة لمستخدمي أنظمة Windows فإنه يتوجب التعديل على الملف php.ini و تفعيل PDP و القواعد البيانات التي نريد إستخدامها بحذف الفاصلة المنقوطة قبل كل إضافة في قسم extension كالآتي:

extension=php_pdo.so
extension=php_pdo_pgsql.so
extension=php_pdo_mysql.so
;extension=php_pdo_sqlite.so

بعدها نعيد تشغيل السيرفر.
الإتصال بقاعدة البيانات:
كما قلنا سابقا أن PDO تستخدم OOP، لهذا فإن عند إستخدامها في PHP علينا أولا إستدعاء الكائن PDO كالآتي:

<?php
$host = "localhost";
$db = "test";
$user = "dev";
$pass = "pass";
$db = new PDO("mysql:host=$host;dbname=$db", $user, $pass);
?>

عند إستدعاء الكائن فإنه يقبل 3 وسائط و 4 إختياري، الوسيط الأول نحدد فيه نوع قاعدة البيانات التي نريد إستخدامها، في حالتنا هذه كانت MySQL، مع عنوان السيرفر الذي يحتوي على قاعدة البيانات و لا ننسى إسم قاعدة البيانات التي نريد إستخدامها.
الوسيط الثاني يكون إسم المستخدم لقاعدة البيانات المستعملة، و الوسيط كلمة السر للمستخدم، أما الوسيط الرابع فهو مصفوفة تحتوي مجموعة من الخيارات التي نريد تطبيقها على إتصال ما.
معالجة الإستثناءات:
في حالة عدم نجاح الإتصال بقاعدة بيانات، سيكون من الجيد معرفة السبب، الكود سيكون كالآتي:

<?php
$host = "localhost";
$db = "test";
$user = "dev";
$pass = "pass";
 try
  {
   $db = new PDO("mysql:host=$host;dbname=$db", $user, $pass);
  }

 catch(PDOException $err)
  {
   die('Error : ' . $err->getMessage());
  }
?>

في السطر 8 سنقوم بمحاولة الإتصال داخل الحاجز try، في حالة خطأ ما ستلتطقه PDOException في المتغير err$، بعدها نقوم بإنهاء عمل السكربت في السطر 13 و إظهار رسالة الخطأ بإستخدام ال method getMessage.
تحديد الخيارات لإتصال:
تتيح لك ال method setAttribute تحديد خيارات على مقبض إتصال، بعض الخيارات تكون متاحة في كل محركات قواعد البيانات المدعومة و بعضها الآخر يكون خاص فقط ببعض المحركات.
مثلا نريد أن نحدد طريقة تتبع الأخطاء بإستخدام Exceptions، بهذا سيكون إستخدام setAttribute كالآتي:

<?php
$host = "localhost";
$db = "test";
$user = "dev";
$pass = "pass";
 try
  {
   $db = new PDO("mysql:host=$host;dbname=$db", $user, $pass);
   $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  }
 catch(Exception $err)
  {
   die('Error : ' . $err->getMessage());
  }
?>

في السطر التاسع، getAttribute تأخذ وسيطين، الأول يكون نوع الخيار و الثاني قيمة الإختيار.
إستخدام bindValue و تحضير الإستعلام:
توفر PDO دالة ممتازة إسمها ()prepare، مهمتها توفير لك إستعلام مترجم تقوم بالإحتفاظ به ايضا في حالة إستخدمته مرة أخرى، مما يوفر لك هذا وقتا، و لن يبقى سوى الإرسال إلى السيرفر.
BindValue مهمتها توفير لك متغيرات داخل إستعلامك، مثال على ما سبق:

<?php
$host = "localhost";
$db = "test";
$user = "dev";
$pass = "pass";
 try
  {
   $db = new PDO("mysql:host=$host;dbname=$db", $user, $pass);
   $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  }
 catch(Exception $err)
  {
   die('Error : ' . $err->getMessage());
  }
$uid = $_GET['user'];
$tid = $_GET['travel'];
$db->prepare("SELECT * FROM Orders where id_travel =:tid and id_user =:uid");
$db->bindValue(':uid', (int)$uid, PDO::PARAM_INT);
$db->bindValue(':tid', (int)$tid, PDO::PARAM_INT);
?>

نقوم أولا بإستقبال رقم المستخدم و رقم رحلته مثلا من نموذج ملئ الرحلة، بعدها في السطرين 18 و 19 نقوم بإستخدام bindValue ال تي تساعدنا بتضمين متغيراتنا داخل ال method prepare، الفائدة الجلية هنا سيكون لك إستعلام محضر سابقا في حالة إستخدامه لاحقا، و الفائدة الأخرى أننا سنتجنب مشكل ال SQL injection.
BindValue عادة تأخذ 3 وسائط، الأول يكون بمثابة متغير نستخدمه داخل ال method prepare، هذا المتغير يكون داخل ” أو ‘ و يبتدأ ب نقطتين مزدوجتين.
الوسيط الثاني يكون القيمة التي سنضمنها للمتغير الخاص ب ال method prepare، الوسيط الأخير نحدد فيه نوع المتغير هل هو عدد أم حروف (PDO::PARAM_STR)، الوسيط الرابع في حال إستخدمناه يكون حجم المتغير في حالة كان من نوع PDO::PARAM_STR.
تنفيذ الإستعلام بإستخدام execute:
آخر شيء بقي هو تنفيذ الإستعلام:

<?php
$host = "localhost";
$db = "test";
$user = "dev";
$pass = "pass";
 try
  {
   $db = new PDO("mysql:host=$host;dbname=$db", $user, $pass);
   $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  }
 catch(Exception $err)
  {
   die('Error : ' . $err->getMessage());
  }
$uid = $_GET['user'];
$tid = $_GET['travel'];
$db->prepare("SELECT * FROM Orders where id_travel =:tid and id_user =:uid");
$db->bindValue(':uid', (int)$uid, PDO::PARAM_INT);
$db->bindValue(':tid', (int)$tid, PDO::PARAM_INT);
$db->execute();
echo "Number of row returned are: ".$db->rowCount();
?>

السطر 21 أضفناه لكي نرى عدد الحقول التي تم إعادتها من الإستعلام.
الفرق بين execute، exec و query:
PDO توفر 3 طرق لتنفيذ إستعلام SQL، كل واحدة لها محل من الإعراب أقصد في الإستخدام كالآتي:
execute: تقوم بتنفيذ إستعلام SQL بإستخدام ال method prepare، هذا يوفر حماية من SQL injection مع الإحتفاظ بالإستعلام من طرف PDO في حالة إستخدامه لاحقا.
في حالة فشلها تعود لنا ب FALSE أما في النجاح تعيد TRUE، إن أردنا أن نعرف عدد الحقول نستخدم ال() method rowCount.
exec: تنفذ إستعلام SQL مباشرة من دون الإعتماد على ال method prepare، خطر الإصابة ب ال SQL injection كبير ما لم يقم المطور بتنقيح مدخلات المستخدم.
تعيد FALSE في حالة الفشل، 0 في حالة لم يكن هناك أي حقل معاد أو عدد الحقول المعادة في حال وجدت.
query: نفس سابقتها exec غير ان القيمة المعادة هنا تكون عبارة عن كائن يحتوي على أسماء الحقول المعادة.
مثال:

<?php
$host = "localhost";
$db = "test";
$user = "dev";
$pass = "pass";
 try
  {
   $db = new PDO("mysql:host=$host;dbname=$db", $user, $pass);
   $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  }
 catch(Exception $err)
  {
   die('Error : ' . $err->getMessage());
  }
$uid = $_GET['user'];
$tid = $_GET['travel'];
foreach($db->query("SELECT FROM Orders") as $row)
 {
  $row['id_user']."\t";
  $row['id_travel']."\t";
  $row['name_travel']."\n";
 }
?>

يمكنك أن تدعو ال method query بـ mysql_fetch_row.
إستخدام fetch، fetchAll:
توفر PDO عدة أشكال لإعادة البيانات من قاعدة البيانات، مما يتيح لك الإختيار في كيفية الوصول إلى حقول القاعدة، المثال الآتي يوضح المقصود:

<?php
$host = "localhost";
$db = "test";
$user = "dev";
$pass = "pass";
 try
  {
   $db = new PDO("mysql:host=$host;dbname=$db", $user, $pass);
   $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  }
 catch(Exception $err)
  {
   die('Error : ' . $err->getMessage());
  }
$uid = $_GET['user'];
$tid = $_GET['travel'];
$db->prepare("SELECT * FROM Orders where id_travel =:tid and id_user =:uid");
$db->bindValue(':uid', (int)$uid, PDO::PARAM_INT);
$db->bindValue(':tid', (int)$tid, PDO::PARAM_INT);
$db->execute();
$row = $db->fetch(PDO::FETCH_ASSOC);
echo $row['user']."\t".$row['email']."\t".$row['address']."\n";
?>

بالنظر إلى السطر 21 نرى ال method fetch، هي تأخذ 3 وسائط لكن عادة نحتاج الوسيط الأول فقط الذي يكون طريقة إستخراج البيانات من القاعدة، هل ستكون على شكل مصفوفة تكون مفهرسة بأسماء الحقول في الجدول، أم مفهرسة على شكل أرقام إبتداءا من 0، هذه بعض القيم من الممكن أن تأخذها ال method fetch:
PDO::FETCH_ASSOC: إعادة نتيجة الإستعلام على شكل مصفوفة مفهرسة بأسماء الحقول الموجودة.
PDO::FETCH_NUM: إعادة نتيجة الإستعلام على شكل مصفوفة مفهرسة بأرقام إبتداءا من 0.
PDO::FETCH_BOTH: إعادة نتيجة الإستعلام على شكل مصفوفة مفهرسة بأسماء الحقول و أرقام.
توجد أشكال أخرى مذكورة في php manual يمكنكم الإطلاع عليها.
ال method fetchAll مهمتها إرجاع جميع القيم الموجودة في حقل ما على شكل مصفوفة، الوسيط الأول نفس سابقتها ال method fetch، أما الوسيطين الآخرين فعادة لانحتاجهما.

<?php
$host = "localhost";
$db = "test";
$user = "dev";
$pass = "pass";
 try
  {
   $db = new PDO("mysql:host=$host;dbname=$db", $user, $pass);
   $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  }
 catch(Exception $err)
  {
   die('Error : ' . $err->getMessage());
  }
$uid = $_GET['user'];
$tid = $_GET['travel'];
$db->prepare("SELECT * FROM Orders where id_travel =:tid and id_user =:uid");
$db->bindValue(':uid', (int)$uid, PDO::PARAM_INT);
$db->bindValue(':tid', (int)$tid, PDO::PARAM_INT);
$db->execute();
$row = $db->fetchAll(PDO::FETCH_ASSOC);
foreach($row as $res)
 foreach($res as $result)
   echo $result['user']."\t".$result['email']."\t".$result['address']."\n";
?>

ال method fetchAll تعيد مصفوفة ذات بعدين، البعد الأول يكون مفهرس بشكل أرقام و البعد الثاني يكون على حسب ما تم تحديده في الوسيط الأول لها، حيث هذا البعد على قيمة حقل أو حقول من إستعلام تم تنفيذه.
في السطر 22 نقوم كل مرة بإستخراج عنصر الذي هو في الحقيقة مؤشر لمصفوفة تحتوي على القيم المعادة، بعدها في السطر 23 نقوم بإستخراج القيم من المصفوفة التي تحتوي على القيم(أي أننا في البعد الثاني للمصفوفة)، أخيرا في السطر 23 نقوم بطباعة القيم.
قطع الإتصال بقاعدة البيانات:
آخر شيء يبقى لنا هو قطع الإتصال مع قاعدة بينات قمنا بالإتصال معها:

<?php
$host = "localhost";
$db = "test";
$user = "dev";
$pass = "pass";
 try
  {
   $db = new PDO("mysql:host=$host;dbname=$db", $user, $pass);
   $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  }
 catch(Exception $err)
  {
   die('Error : ' . $err->getMessage());
  }
$db = NULL;
?>

بكل بسطاة نقوم بتضمين NULL إلى المتغير الذي كان يحمل حدث إنشاء الإتصال.
ملاحظة:
هذه المقالة تحتاج إلى تعديل و تنقيح في اللغة لمن يريد التعديل فهو مرحب به.
مراجع:
http://www.php.net/manual/en/book.pdo.php
http://www.phpeveryday.com/articles/PDO-Error-Handling-P544.html
http://codeassembly.com/Are-you-still-worried-about-sql-injection-?/
http://stackoverflow.com/questions/134099/are-pdo-prepared-statements-sufficient-to-prevent-sql-injection
http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/

التصنيفات :برمجة الوسوم:, , ,
  1. 25 يونيو 2011 الساعة 9:02 ص

    شرح ولا اروع ومعلومات مفيدة جدا ً جزاك الله الف خير اخي الكريم

  2. emad
    11 سبتمبر 2014 الساعة 11:20 ص

    الله يكثر من امثالك عزيزي لما تنشر العلم المفيد

  1. No trackbacks yet.

أضف تعليقاً

إملأ الحقول أدناه بالمعلومات المناسبة أو إضغط على إحدى الأيقونات لتسجيل الدخول:

WordPress.com Logo

أنت تعلق بإستخدام حساب WordPress.com. تسجيل خروج   / تغيير )

صورة تويتر

أنت تعلق بإستخدام حساب Twitter. تسجيل خروج   / تغيير )

Facebook photo

أنت تعلق بإستخدام حساب Facebook. تسجيل خروج   / تغيير )

Google+ photo

أنت تعلق بإستخدام حساب Google+. تسجيل خروج   / تغيير )

Connecting to %s

%d مدونون معجبون بهذه: