تخطَّ إلى المحتوى

الاتصال بقاعدة بيانات MySQL في PHP باستخدام PDO

الدرس 8 من 12 · PHP

حتى الآن، كل البيانات في برامجك تختفي بمجرد انتهاء تشغيل الصفحة. لكن المواقع الحقيقية تتذكر: الحسابات، والتعليقات، والطلبات. هذه البيانات تُحفظ في قاعدة بيانات (Database). في هذا الدرس الأخير من كورس PHP ستربط صفحاتك بقاعدة بيانات MySQL، وتقرأ منها وتكتب فيها بطريقة آمنة.

قاعدة البيانات مثل أرشيف منظم فيه جداول، وكل جدول مثل جدول Excel: أعمدة (الاسم، العمر) وصفوف (كل صف طالب). وحتى تتحدث معها، تستخدم لغة اسمها SQL:

SELECT name, age FROM students;

و PHP تعمل كـ وسيط: تستقبل طلب الزائر، وترسل استعلام SQL لقاعدة البيانات، ثم تحوّل النتيجة إلى صفحة HTML.

إذا ثبّت XAMPP كما في الدرس الأول، فعندك MySQL جاهزة. شغّلها من لوحة التحكم، ثم افتح أداة phpMyAdmin على http://localhost/phpmyadmin، وأنشئ قاعدة بيانات باسم school، ونفّذ فيها هذا الأمر من تبويب SQL:

CREATE TABLE students (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
age INT NOT NULL
);

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

PHP تتصل بقواعد البيانات عبر PDO (PHP Data Objects)، وهي واجهة حديثة وآمنة تعمل مع MySQL وغيرها. نضع الاتصال في ملف مستقل لنستخدمه في كل الصفحات:

db.php
<?php
$host = "localhost";
$dbname = "school";
$user = "root";
$password = ""; // في XAMPP كلمة المرور فارغة افتراضياً
try {
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $user, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
} catch (PDOException $e) {
die("فشل الاتصال بقاعدة البيانات");
}
  • النص الأول يسمى DSN: نوع القاعدة (mysql) والخادم واسم القاعدة. و charset=utf8mb4 ضروري لتُحفظ الحروف العربية سليمة.
  • ERRMODE_EXCEPTION: أي خطأ في قاعدة البيانات يرمي استثناءً واضحاً بدل أن يمر بصمت.
  • FETCH_ASSOC: النتائج تأتي كمصفوفات ترابطية، مثل $row["name"].
  • try و catch: إذا فشل الاتصال، نعرض رسالة عامة. لا تعرض تفاصيل الخطأ $e->getMessage() للزوار، لأنها قد تكشف معلومات عن خادمك.

ثم في أي صفحة تحتاج قاعدة البيانات، اكتب في أولها require "db.php";.

<?php
require "db.php";
$stmt = $pdo->query("SELECT id, name, age FROM students ORDER BY id");
$students = $stmt->fetchAll();
foreach ($students as $student) {
echo $student["id"] . " - " . $student["name"] . " (" . $student["age"] . " سنة)\n";
}

إذا كان الجدول فيه ثلاثة طلاب، سيكون الناتج:

الناتج
1 - سارة (14 سنة)
2 - عمر (15 سنة)
3 - منى (13 سنة)

لاحظ أن fetchAll تعطيك مصفوفة متداخلة: قائمة صفوف، وكل صف مصفوفة ترابطية. هذا بالضبط ما تعلمته في درس المصفوفات.

عرض البيانات في جدول HTML

Section titled “عرض البيانات في جدول HTML”

هنا يجتمع كل ما تعلمته في الكورس:

students.php
<?php require "db.php"; ?>
<?php $students = $pdo->query("SELECT * FROM students ORDER BY id")->fetchAll(); ?>
<table>
<tr><th>الرقم</th><th>الاسم</th><th>العمر</th></tr>
<?php foreach ($students as $s): ?>
<tr>
<td><?= $s["id"] ?></td>
<td><?= htmlspecialchars($s["name"]) ?></td>
<td><?= $s["age"] ?></td>
</tr>
<?php endforeach; ?>
</table>

لاحظ htmlspecialchars حول الاسم: البيانات في القاعدة كتبها مستخدمون في الأصل، فهي مدخلات غير موثوقة أيضاً.

الإضافة الآمنة: الاستعلامات المجهزة

Section titled “الإضافة الآمنة: الاستعلامات المجهزة”

لإضافة طالب جديد من نموذج، قد يخطر لك أن تكتب:

// خطير جداً، لا تفعل هذا أبداً
$pdo->query("INSERT INTO students (name, age) VALUES ('$name', $age)");

إذا كتب المهاجم في حقل الاسم نصاً يحتوي أوامر SQL، فسيصبح جزءاً من الاستعلام، ويستطيع قراءة أو حذف قاعدة بياناتك كلها! هذا الهجوم اسمه حقن SQL (SQL Injection).

الحل: الاستعلامات المجهزة (Prepared Statements). تكتب الاستعلام بعلامات ? مكان القيم، ثم ترسل القيم منفصلة:

<?php
require "db.php";
$name = "سارة";
$age = 14;
$stmt = $pdo->prepare("INSERT INTO students (name, age) VALUES (?, ?)");
$stmt->execute([$name, $age]);
echo "تمت الإضافة، رقم الطالب: " . $pdo->lastInsertId();

قاعدة البيانات تتعامل مع القيم المرسلة على أنها بيانات فقط، مهما كان محتواها، فلا يمكن أن تتحول إلى أوامر. تخيّل الفرق بين أن تعطي موظف البنك ورقة كتبها شخص غريب ليقرأها بصوت عالٍ وينفّذها، وبين أن تملأ أنت استمارة البنك الرسمية التي لا يُكتب في خاناتها إلا البيانات.

ويمكنك استخدام أسماء بدل علامات ?، وهي أوضح مع القيم الكثيرة:

<?php
$stmt = $pdo->prepare("SELECT name FROM students WHERE age >= :age");
$stmt->execute(["age" => 14]);
foreach ($stmt->fetchAll() as $row) {
echo $row["name"] . "\n";
}
الناتج
سارة
عمر

بنفس الطريقة الآمنة:

<?php
$stmt = $pdo->prepare("UPDATE students SET age = ? WHERE id = ?");
$stmt->execute([16, 2]);
echo "عدد الصفوف المعدلة: " . $stmt->rowCount() . "\n";
$stmt = $pdo->prepare("DELETE FROM students WHERE id = ?");
$stmt->execute([3]);
echo "عدد الصفوف المحذوفة: " . $stmt->rowCount() . "\n";
الناتج
عدد الصفوف المعدلة: 1
عدد الصفوف المحذوفة: 1

rowCount تخبرك بعدد الصفوف التي تأثرت. وانتبه جيداً لشرط WHERE في التعديل والحذف: إذا نسيته، سيُعدَّل أو يُحذف كل الجدول!

ابنِ صفحة صغيرة لإدارة الطلاب:

  1. أنشئ قاعدة school وجدول students كما سبق، والملف db.php.
  2. أنشئ students.php يعرض الطلاب في جدول.
  3. أضف فوق الجدول نموذجاً بطريقة POST فيه حقلا الاسم والعمر، وعند الإرسال تحقق من البيانات كما في درس النماذج، ثم أضف الطالب باستعلام مجهز.
  4. تحدٍّ: أضف بجانب كل طالب زر «حذف» داخل نموذج POST صغير يرسل id الطالب.
  • ظهور الحروف العربية كعلامات استفهام: نسيت charset=utf8mb4 في الاتصال، أو أن الجدول نفسه ليس بترميز utf8mb4.
  • وضع المتغيرات داخل نص الاستعلام: ثغرة حقن SQL. استخدم prepare و execute دائماً.
  • عرض تفاصيل الخطأ للزوار: رسائل الأخطاء الكاملة للمطور فقط أثناء التطوير، لا في الموقع المنشور.

سؤال

ما الطريقة الآمنة لإضافة اسم كتبه المستخدم إلى قاعدة البيانات؟

سؤال

لماذا نضيف charset=utf8mb4 في نص الاتصال؟

  • PHP تتصل بقاعدة البيانات عبر PDO، مع charset=utf8mb4 ووضع الاستثناءات.
  • query ثم fetchAll لقراءة البيانات، والنتيجة مصفوفة صفوف ترابطية.
  • prepare و execute لأي استعلام فيه مدخلات من المستخدم، لمنع حقن SQL.
  • htmlspecialchars عند عرض البيانات، و WHERE إجباري في التعديل والحذف.
  • أنت الآن تبني صفحات ديناميكية كاملة: نموذج يستقبل البيانات، وقاعدة بيانات تحفظها.