3.1MySQL و PDO : الاتصال والاستعلامات المحضرة (prepared statements)
الأساسي في 30 ثانية
- PDO هو الواجهة الحديثة : اتصال كائني موحد (new PDO)، متعدد القواعد، وأخطاء على شكل استثناءات مع ERRMODE_EXCEPTION.
- الاستعلام المحضر (prepared statement) غير قابل للتفاوض : علامات :nom أو ?، والبيانات تُمرَّر إلى execute() — القاعدة تعاملها كبيانات خالصة، أبدا ككود.
- يُقرأ CRUD بأربعة أفعال : INSERT / SELECT / UPDATE / DELETE، والنتائج عبر fetch() (سطر واحد) أو fetchAll() (الكل).
حالة مصغرة متسلسلة — رياد ينقذ متجره من حقنة
رياد، مطور وب في قسنطينة، يدير طلبات متجر بدمج البريد المُدخَل في SELECT — اختبار بعلامة اقتباس مفخخة أعاد له جدول الزبناء كاملا. أعاد كتابة كل شيء بـ PDO : اتصال مركزي في ملف config.php مع ERRMODE_EXCEPTION، واستعلامات prepare() + execute() بعلامات مسماة، و fetch/fetchAll للقراءة. المتجر نفسه، وقاعدة أصبحت منيعة عبر هذا المتجه.
هذه الحالة نفسها تعود في الدرس والمخطط والنص الجاهز وتحدي 48 ساعة.
1. MySQL و PDO والاتصال المركزي
التطبيق الحقيقي يخزن بشكل دائم بياناته : MySQL (أو MariaDB)، قاعدة علائقية، تنظم البيانات في جداول (أعمدة منمطة)، وأسطر (سجلات)، تُستعلَم بـ SQL : SELECT (قراءة)، وINSERT (إنشاء)، وUPDATE (تعديل)، وDELETE (حذف) — عمليات CRUD الأربع.
المقاربة الحديثة هي PDO (PHP Data Objects) : واجهة موحدة وكائنية تعمل مع عدة قواعد (قابلية نقل)، واستعلامات محضرة أنيقة بعلامات مسماة، وتدبير أخطاء بـ الاستثناءات في وضع PDO::ERRMODE_EXCEPTION (try/catch نظيف). ومنعكسا رياد : تركيز الاتصال (ملف config/اتصال واحد يُعاد استعماله، أبدا اتصال لكل صفحة) وعدم إيداع بيانات الاعتماد في Git أبدا — فهي تعيش خارج الكود (متغيرات البيئة)، أبدا داخل الكود.
2. الاستعلامات المحضرة : prepare + execute، دائما
الخطر : بناء SQL بـ دمج بيانات المستعمل ('SELECT … WHERE email = ' . $email) يخلق ثغرة حرجة هي حقن SQL (SQL injection) — المهاجم يحقن SQL تنفذه القاعدة (قراءة، تعديل، تدمير). والحل هو الاستعلام المحضر (prepared statement) : نُحضِّر بـ علامات (? أو مسماة :email) — $stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email') — ثم نُنفِّذ بتمرير البيانات منفصلة — $stmt->execute(['email' => $email]) — ونسترجع بـ fetch() أو fetchAll().
القاعدة مطلقة : كل استعلام فيه بيانات متغيرة يجب تحضيره ؛ ولا تدمج أبدا بيانات في SQL. يطبقها رياد على CRUD كاملا : INSERT لطلب، وSELECT للزبناء، وUPDATE للحالة، وDELETE لمسودة — كل execute() يستقبل قيمه، وSQL لا يحوي سوى علامات.
🔎 مثال ملموس — اتصال PDO و SELECT محضر
يبحث رياد عن زبون ببريده، بكل أمان.
PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
$stmt = $pdo->prepare('SELECT id, nom, email FROM clients WHERE email = :email');
$stmt->execute(['email' => $emailSaisi]);
$client = $stmt->fetch();
القراءة : اتصال باستثناءات، وعلامة :email، وبيانات في execute()، وسطر واحد عبر fetch().
الدرس : حتى مع بريد مفخخ، لا ترى القاعدة سوى بيانة — لا كود مُنفَّذ.
مفردات القسم
- قاعدة البيانات (database) / MySQL
- نظام يخزن البيانات بشكل دائم ؛ MySQL/MariaDB : قاعدة علائقية (جداول، أسطر، أعمدة) تُستعلَم بـ SQL (SELECT/INSERT/UPDATE/DELETE = CRUD).
مثال بسيط: جدول الطلبات في متجر رياد.
ما ليس كذلك: جدول PHP يُفرَّغ مع كل نهاية طلب.
رد فعل: التخزين في قاعدة متى وجب أن تدوم البيانة.
- PDO
- PHP Data Objects : واجهة حديثة وموحدة وكائنية للاتصال بالقواعد وتنفيذ الاستعلامات ؛ متعددة القواعد، واستثناءات، وعلامات مسماة.
مثال بسيط: اتصال رياد المركزي مع ERRMODE_EXCEPTION.
ما ليس كذلك: دوال mysql_* القديمة المحذوفة.
رد فعل: دائما PDO مع ERRMODE_EXCEPTION.
- حقن SQL (SQL injection)
- ثغرة حرجة : بيانات مدمجة في SQL تُفسَّر ككود مُنفَّذ (قراءة، تعديل، تدمير القاعدة).
مثال بسيط: علامة الاقتباس المفخخة التي أفرغت جدول زبناء رياد.
ما ليس كذلك: مجرد زلة عرض دون خطورة.
رد فعل: لا تدمج أبدا بيانة في SQL.
- الاستعلام المحضر (prepared statement)
- استعلام بعلامات (? أو :nom) : يرسل prepare() الكود، ويرسل execute() البيانات منفصلة — فتعاملها القاعدة كبيانات خالصة.
مثال بسيط: prepare('… WHERE email = :email') ثم execute(['email' => $emailSaisi]).
ما ليس كذلك: دمج '…' . $email في سلسلة SQL.
رد فعل: prepare + execute لكل استعلام فيه بيانات متغيرة.
- CRUD / fetch
- Create (INSERT)، وRead (SELECT)، وUpdate (UPDATE)، وDelete (DELETE) ؛ والقراءة عبر fetch() (سطر واحد) أو fetchAll() (الكل).
مثال بسيط: ينشئ رياد طلب اختبار ويقرؤه ويعدله ثم يحذفه.
ما ليس كذلك: SELECT واحد منسوخ دون كتابة.
رد فعل: إتقان الأفعال الأربعة قبل أي إطار.
في التطبيق — تخزين البيانات الدائم مع MySQL و PDO
- أنشئوا قاعدة وجدولا في MySQL (عبر phpMyAdmin) ؛ واتصلوا من PHP بـ PDO (المضيف، القاعدة، المستعمل، كلمة السر خارج الكود).
- نفذوا CRUD باستعلامات محضرة (prepared statements) : INSERT (إنشاء)، وSELECT (قراءة، fetch/fetchAll)، وUPDATE (تعديل)، وDELETE (حذف).
- استعملوا منهجيا علامات (:nom أو ؟) ومرروا البيانات عبر execute() — ولا تدمجوا أبدا بيانات في SQL.
- دبروا الأخطاء بـ try/catch (ERRMODE_EXCEPTION) وتأكدوا أن مدخَلا مفخخا لم يعد يكسر شيئا.
نص جاهز للقول — عرض الوصول للقاعدة بـ PDO (30 ثانية)
« الطلبات في MySQL عبر PDO : اتصال مركزي باستثناءات، وCRUD كامل باستعلامات محضرة بعلامات مسماة، وقراءة بـ fetch و fetchAll. مُختبَر بمدخَل مفخخ : تعامله القاعدة كبيانة بسيطة، والحقن مستحيل. »
يُقال في المراجعة ؛ ويُعرَض prepare/execute والاختبار المفخخ.
نموذج نسخ-لصق — PDO + CRUD محضر
PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
// CREATE
$stmt = $pdo->prepare('INSERT INTO clients (nom, email) VALUES (:nom, :email)');
$stmt->execute(['nom' => $nom, 'email' => $email]);
// READ
$stmt = $pdo->prepare('SELECT id, nom, email FROM clients WHERE email = :email');
$stmt->execute(['email' => $email]);
$client = $stmt->fetch();
$tous = $pdo->query('SELECT id, nom FROM clients')->fetchAll();
// UPDATE + DELETE
$stmt = $pdo->prepare('UPDATE clients SET nom = :nom WHERE id = :id');
$stmt->execute(['nom' => $nom, 'id' => $id]);
$stmt = $pdo->prepare('DELETE FROM clients WHERE id = :id');
$stmt->execute(['id' => $id]);
مستقل
يكفي CRUD زبناء بـ PDO محضر لتأمين عروض الأسعار المخزنة.
مقاولة صغيرة في قسنطينة
يركز رياد اتصال المتجر ويحضر كل استعلام في المكتب الخلفي.
فريق منتج
فرض ملف اتصال واحد لاستعلامات متسقة بين الوحدات.
مبتدئ
هذا الأسبوع : جدول واحد، واتصال PDO واحد، و SELECT محضر يُقرأ بـ fetch().
✓ ممارسات جيدة
- تركيز اتصال PDO (مكان واحد، يُعاد استعماله في كل مكان).
- التحضير منهجيا : علامات + execute()، ولا دمج أبدا.
- تفعيل ERRMODE_EXCEPTION والقراءة بـ fetch/fetchAll حسب الحاجة.
✗ أخطاء شائعة
- دمج بيانات في SQL، حتى « للاختبار فقط » (حقن حرج).
- إنشاء اتصال PDO في كل ملف (تكرار، وبيانات اعتماد مبعثرة).
- استعمال دوال mysql_* القديمة بدل PDO.
النقاط الأساسية
- التطبيق الحقيقي يخزن بياناته في قاعدة. MySQL/MariaDB (علائقية : جداول، أسطر) تُستعلَم بـ SQL (SELECT/INSERT/UPDATE/DELETE = CRUD).
- PDO = الواجهة الحديثة والآمنة للاتصال وتنفيذ الاستعلامات (متعددة القواعد، استثناءات، علامات مسماة).
- المركزي : الاستعلامات المحضرة (prepared statements). دمج البيانات = ثغرة حرجة هي حقن SQL (SQL injection). علامات (:nom / ؟) + بيانات في execute().
- قاعدة مطلقة : كل استعلام فيه بيانات متغيرة يجب تحضيره ؛ و fetch/fetchAll للقراءة — مهارة مركزية في PHP مع قاعدة.
تحدي 48 ساعة — طبّق غدا
اربطوا PDO واحدا باستثناءات ونفذوا CRUD : INSERT + SELECT محضرين مُختبَرا بـ مدخَل مفخخ.
- أميز prepare() (الكود) عن execute() (البيانات).
- CRUD عندي محضر كاملا، دون دمج.
- اتصالي مركزي، والأخطاء باستثناءات.
أسئلة شائعة
ما هو حقن SQL (SQL injection)، ولماذا الاستعلامات المحضرة هي الحل ؟
حقن SQL (SQL injection) هجوم تُرسَل فيه بيانات المهاجم (حقل، معامل URL) فـ تُنفَّذ كـ SQL لأنكم دمجتموها في الاستعلام : تجاوز دخول، وقراءة جداول أو تدميرها. السبب : خلط الكود والبيانات في سلسلة واحدة. والاستعلام المحضر (prepared statement) يفصل بينهما : يرسل prepare() الكود مع علامات، ويرسل execute() البيانات على حدة — فتعاملها القاعدة كبيانات خالصة، حتى لو مفخخة. إنها الحماية، وتُطبَّق دائما، دون استثناء.
لماذا نستعمل PDO، وكيف نهيكل الوصول للقاعدة ؟
PDO هو المقاربة الحديثة : تجريد متعدد القواعد (نفس API لـ MySQL و PostgreSQL…)، واستعلامات محضرة أنيقة بعلامات مسماة، و API كائنية متسقة، وأخطاء بـ الاستثناءات (try/catch). وللهيكلة : ركزوا الاتصال (ملف config/اتصال واحد أو صنف، يُعاد استعماله في كل مكان)، وبيانات الاعتماد خارج المستودع (البيئة)، وأعيدوا استعمال PDO نفسه في كل الكود — انتهى زمن الاتصال المُعاد إنشاؤه لكل صفحة.
ماذا يفصل الاستعلام المحضر PDO ؟