انتقل إلى محتوى الدرس
الدرس 96
دورة PHP

بناء لوحة تحكم CRUD متكاملة ومحمية باستخدام PDO في PHP 8Building a Protected PHP CRUD Dashboard with PDO and Flash Alerts

اجمع التوجيه وحراسة المصادقة واستعلامات PDO المحضرة وتقسيم الصفحات وحماية CSRF وXSS في مشروع لوحة تحكم CRUD متكاملة بـ PHP 8.
أقسام الدرس
6
الأمثلة
6
اللغة
العربية
تاريخ النشر
غير محدد
المشاهدات
0
محتوى الدرس
في هذا الدرس 6تصفّح المواضيع

يقوم استعلام العرض في لوحة التحكم بفلترة المهام حسب معرف المستخدم المسجل user_id، وتطبيق بحث LIKE اختياري، وتقسيم الصفحات عبر LIMIT وOFFSET.

  • اضبط رقم الصفحة عبر $page = max(1, (int) ($_GET["page"] ?? 1)) لمنع الأرقام السالبة أو الصفرية.
  • رمّز كل حقل نصي عبر htmlspecialchars($row["title"], ENT_QUOTES, "UTF-8") عند طباعته في جدول HTML.
  • قيّد الاستعلام بالشرط WHERE user_id = :uid ليرى كل مستخدم سجلاته الخاصة فقط.

تحسب الصفحة 2 إزاحة قدرها 10 سجلات، وتبطل دالة htmlspecialchars() وسم السكريبت الخبيث.

مخطط توضيحي: محاكي لوحة تحكم CRUD المتكاملة وفحص ملكية السجل في PHP 8
PHP
<?php

$page = max(1, (int) ($_GET['page'] ?? 2));
$perPage = 10;
$offset = ($page - 1) * $perPage;

$rawTitle = '<script>alert(1)</script>Deploy App';
$safeTitle = htmlspecialchars($rawTitle, ENT_QUOTES, 'UTF-8');

echo "Page $page -> LIMIT $perPage OFFSET $offset\n";
echo "Escaped Row: $safeTitle\n";

تجمع عملية إضافة سجل جديد بين فحص توكن CSRF، والتحقق من المدخلات، واستعلام INSERT محضر مرتبط بـ $_SESSION["user_id"]، ثم تحويل 303 See Other.

  • خذ معرف المالك user_id دائماً من جلسة الخادم الموثوقة $_SESSION["user_id"] وليس من حقل مخفي في النموذج!
  • تحقق من أن النصوص المطلوبة غير فارغة وضمن الطول المسموح به للعمود.
  • احفظ رسالة نجاح مؤقتة في الجلسة قبل التحويل إلى قائمة السجلات.

تم ربط المهمة مباشرة بمعرف المستخدم الموثق في الجلسة وإدراجها باستعلام محضر.

PHP
<?php

$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE tasks (id INTEGER PRIMARY KEY, user_id INT, title TEXT)');

$sessionUserId = 42;
$title = trim('Ship PHP 8.4 Course');

$stmt = $pdo->prepare('INSERT INTO tasks (user_id, title) VALUES (:uid, :title)');
$stmt->execute(['uid' => $sessionUserId, 'title' => $title]);
echo "Inserted Task #" . $pdo->lastInsertId() . " for User #$sessionUserId\n";

تثبت المصادقة هوية المستخدم، بينما تثبت الصلاحية (Authorization) ملكيته للسجل الذي يحاول تعديله أو حذفه؛ وإهمال ذلك يسبب ثغرة الوصول المباشر غير الآمن للكائنات (IDOR)!

  • أضف دائماً الشرط AND user_id = :user_id في كل استعلام SELECT وUPDATE وDELETE على جداول المستخدمين.
  • إذا غيّر مستخدم الرقم في الرابط من ?id=10 إلى ?id=11، سيرجع الاستعلام صفر صفوف ويُرفض الطلب بالرمز 403.
  • لا تثق أبداً بمعرف سجل قادم من $_GET أو $_POST دون التحقق من ملكيته.

لأن المهمة رقم 10 تخص المستخدم 42، فإن الاستعلام بمعرف المهاجم 99 يرجع false ويحبط ثغرة IDOR.

PHP
<?php

$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE tasks (id INTEGER PRIMARY KEY, user_id INT, title TEXT)');
$pdo->exec("INSERT INTO tasks VALUES (10, 42, 'Owner Task')");

// Attacker (user_id = 99) tries to fetch Task #10
$stmt = $pdo->prepare('SELECT * FROM tasks WHERE id = :id AND user_id = :uid');
$stmt->execute(['id' => 10, 'uid' => 99]);
$task = $stmt->fetch(PDO::FETCH_ASSOC);
echo $task ? "Loaded\n" : "403 Forbidden: IDOR Blocked!\n";

عند عرض نموذج التعديل، املأ خاصية value للحقول عبر htmlspecialchars($old["title"] ?? $task["title"], ENT_QUOTES, "UTF-8") وأرسل التعديلات بطلب POST.

  • قدّم القيمة المؤقتة $old["title"] على قيمة قاعدة البيانات $task["title"] حتى لا تضيع تعديلات المستخدم عند حدوث خطأ تحقق.
  • قيّد استعلام التحديث UPDATE بالشرط المزدوج WHERE id = :id AND user_id = :uid.
  • حوّل الصفحة بنمط PRG فور نجاح التحديث.

قام المالك الشرعي 42 بتحديث المهمة 10 بنجاح، وأكدت rowCount() تعديل صف واحد.

مخطط تدفق التنفيذ العملي لقسم تعديل السجلات الحالية وتعبئة النموذج بأمان في لغة PHP
PHP
<?php

$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE tasks (id INTEGER PRIMARY KEY, user_id INT, title TEXT)');
$pdo->exec("INSERT INTO tasks VALUES (10, 42, 'Draft Lesson')");

$stmt = $pdo->prepare('UPDATE tasks SET title = :t WHERE id = :id AND user_id = :u');
$stmt->execute(['t' => 'Published Lesson', 'id' => 10, 'u' => 42]);

echo "Updated Rows: " . $stmt->rowCount() . "\n";

لا تحذف سجلات قاعدة البيانات أبداً عبر طلب GET (مثل ?action=delete&id=10)؛ بل ضع زر الحذف دائماً داخل نموذج <form method="POST"> محمي بتوكن CSRF وفحص الملكية.

  • روابط الحذف عبر GET قد تُستدعى عن طريق الخطأ من المتصفح أو عبر هجمات تزوير الطلبات.
  • تحقق من توكن CSRF ومن شرط الملكية WHERE id = :id AND user_id = :uid قبل تنفيذ الحذف.
  • اعرض نافذة تأكيد للمستخدم قبل إرسال نموذج الحذف.

يحذف استعلام DELETE المحضر المهمة 10 فقط بعد التأكد من ملكية المستخدم 42 لها.

PHP
<?php

$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE tasks (id INTEGER PRIMARY KEY, user_id INT, title TEXT)');
$pdo->exec("INSERT INTO tasks VALUES (10, 42, 'Temporary Task')");

$stmt = $pdo->prepare('DELETE FROM tasks WHERE id = :id AND user_id = :u');
$stmt->execute(['id' => 10, 'u' => 42]);

echo "Deleted Rows: " . $stmt->rowCount() . "\n";

تهانينا على إتمام كورس PHP 8.4 الحديث الشامل على منصة CloseTag! اعتمد هذه القائمة المرجعية الهندسية في كل مشروع ويب تبنيه.

  • فعّل declare(strict_types=1); والتحميل التلقائي PSR-4 عبر Composer والأنواع الصارمة في كل الأصناف.
  • استخدم PDO مع ERRMODE_EXCEPTION وتعطيل المحاكاة وفحص ملكية السجل في كل استعلام.
  • احمِ جميع النماذج والجلسات بـ password_hash() وتوكنات CSRF وترميز htmlspecialchars() ونمط PRG.

الجمع بين ميزات PHP 8.4 الحديثة واستعلامات PDO المحضرة وطبقات الحماية الأمنية وأنماط تجربة المستخدم يمنحك تأسيساً متكاملاً لبناء تطبيقات الويب.

عملية CRUDطريقة طلب HTTPالقواعد الأمنية وقواعد PDO الإلزامية
Read / ListGET /tasksAuth guard + WHERE user_id = :u + LIMIT/OFFSET + htmlspecialchars()
CreatePOST /tasksAuth guard + CSRF check + validate + INSERT + 303 PRG redirect
UpdatePOST /tasks/updateAuth guard + CSRF check + WHERE id = :id AND user_id = :u + 303 PRG
DeletePOST /tasks/deleteAuth guard + CSRF check + WHERE id = :id AND user_id = :u + 303 PRG

PHP
<?php

$pillars = [
    'Language' => 'PHP 8.4 Strict Types, Match, Enums & OOP',
    'Database' => 'PDO Prepared Statements & Transactions',
    'Security' => 'Argon2id/Bcrypt, CSRF, XSS & IDOR Defense',
    'UX & API' => 'PRG Flash Flow & Fetch JSON Endpoints',
];
foreach ($pillars as $area => $std) {
    echo str_pad($area, 8) . " : $std\n";
}
خلاصة المعرفة ومحطة الانتقال

ملخص الدرس

حصاد الدرس

مراجعة مركّزة لأهم المفاهيم والقواعد الهندسية التي أتقنتها في هذا الدرس قبل التطبيق العملي

ماذا تعلّمت في هذا الدرس؟

تهانينا على إتمام كورس PHP 8.4 الحديث الشامل على منصة CloseTag! اعتمد هذه القائمة المرجعية الهندسية في كل مشروع ويب تبنيه. فعّل declare(strict_types=1); والتحميل التلقائي PSR-4 عبر Composer والأنواع الصارمة في كل الأصناف. استخدم PDO مع ERRMODE_EXCEPTION وتعطيل المحاكاة وفحص ملكية السجل في كل استعلام. احمِ جميع النماذج والجلسات بـ password_hash() وتوكنات CSRF وترميز htmlspecialchars() ونمط PRG.

مشاركة

هل كان هذا الدرس مفيدًا؟

جارٍ تحميل الآراء…

ابنِ لغتك البرمجية

المصطلحات4

راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.

0 / 4 مفهومةأول بطاقة، أول خطوة
01 CRUD Web Project مشروع لوحة تحكم CRUD
اجمع التوجيه وحراسة المصادقة واستعلامات PDO المحضرة وتقسيم الصفحات وحماية CSRF وXSS في مشروع لوحة تحكم CRUD متكاملة بـ PHP 8.
للمراجعة
02 Listing Records with Search and Pagination عرض السجلات مع البحث الفوري وتقسيم الصفحات
المفهوم الأساسي لقسم عرض السجلات مع البحث الفوري وتقسيم الصفحات في لغة PHP الحديثة.
للمراجعة
03 Creating Records with CSRF and PRG إضافة سجل جديد مع التحقق وحماية CSRF ونمط PRG
الصياغة البرمجية والتطبيق العملي المباشر لقسم إضافة سجل جديد مع التحقق وحماية CSRF ونمط PRG.
للمراجعة
04 Enforcing Row Ownership Authorization التحقق من صلاحية ملكية السجل قبل التعديل والحذف
قواعد التحقق وأفضل الممارسات القياسية الخاصة بقسم التحقق من صلاحية ملكية السجل قبل التعديل والحذف.
للمراجعة

اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.

حروف قليلة. معنى كامل.

الاختصارات4

راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.

0 / 4 مفهومةأول بطاقة، أول خطوة
01 PHP PHP
PHP: Hypertext Preprocessor
لغة البرمجة النصية على جانب الخادم لبناء تطبيقات الويب الديناميكية وواجهات البرمجة.
للمراجعة
02 CLI CLI
Command-Line Interface
واجهة سطر الأوامر التي تتيح تشغيل ملفات PHP مباشرة عبر الطرفية.
للمراجعة
03 FPM FPM
FastCGI Process Manager
مدير عمليات FastCGI عالي الأداء لمعالجة طلبات خادم الويب في PHP.
للمراجعة
04 HTML HTML
HyperText Markup Language
لغة الترميز القياسية التي يولدها محرك PHP ويرسلها إلى متصفح العميل بعد التنفيذ.
للمراجعة

اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.

من الفهم للتطبيق

مهامك قبل الاختبار

خطوة صغيرة كل مرة. وإنجاز تقدر ترجع له.

0/2مهام منجزة
  1. أنشئ ملف PHP محلياً ونفّذ المثال البرمجي المشروح في قسم عرض السجلات مع البحث الفوري وتقسيم الصفحات للتحقق من المخرجات.

  2. بدّل بين الحالات الثلاث في المحاكي التفاعلي ضمن قسم إضافة سجل جديد مع التحقق وحماية CSRF ونمط PRG وقارن مخرجات التنفيذ.

تأشير المهمة إقرار منك بإتمامها، وليس تصحيحًا آليًا للكود. يُحفظ تقدمك في حسابك.

مساحة التحدي

اختبر فهمك.
ووصلها للإتقان.

كل سؤال خطوة لقدّام. اختار، اكتب كود، وشوف نتيجتك فورًا.

8أسئلة 1أنواع تحديات 100%هدف الإتقان
تصحيح تلقائي. فرص بلا حدود.

أكمل المهام، واجمع كل الإجابات الصحيحة. أقل من 100%؟ راجع وأعد المحاولة.

الأسئلة متاحة للاطلاع. سجّل الدخول للتفاعل وحفظ تقدمك.

لمحة عن التحدي8 أسئلة تنتظرك
01

ما هي ثغرة الوصول المباشر غير الآمن للكائنات (IDOR) في لوحة تحكم CRUD؟

تحدث ثغرة IDOR عندما يفحص الاستعلام WHERE id = :id فقط دون التحقق من ملكية السجل عبر AND user_id = :session_user_id.

02

كيف تمنع ثغرة IDOR عند تحديث أو حذف مهمة في استعلام SQL؟

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

03

لماذا يجب تنفيذ عمليات الحذف عبر نموذج POST محمي بتوكن CSRF بدلاً من رابط GET مثل /tasks/delete?id=10؟

طلبات GET مخصصة للقراءة فقط، أما العمليات التدميرية كالحذف فتتطلب POST مع حماية CSRF.

04

كيف تحسب قيمة الإزاحة OFFSET في استعلام تقسيم الصفحات عندما يكون لديك رقم الصفحة $page (يبدأ من 1) وعدد العناصر $perPage؟

في الصفحة الأولى تكون الإزاحة (1 - 1) * 10 = 0، وفي الصفحة الثانية تكون 10.

05

لماذا نكتب $page = max(1, (int) ($_GET["page"] ?? 1)) قبل حساب إزاحة الصفحات OFFSET؟

الإزاحة السالبة تسبب خطأ في استعلام قاعدة البيانات.

06

عند طباعة عناوين المهام التي أدخلها المستخدم داخل جدول HTML أو داخل خاصية value لحقل إدخال، أي دالة يجب أن تغلف المخرجات؟

ترميز محارف HTML الخاصة يمنع ثغرات XSS المخزنة في الجداول والحقول.

07

من أين يجب أن يحصل متحكم الإضافة أو التعديل على معرف المستخدم الحالي user_id؟

بيانات جلسة الخادم محفوظة على السيرفر ولا يمكن للمتصفح التلاعب بها.

08

ماذا ترجع $stmt->rowCount() عندما يحاول مهاجم (المستخدم 99) تنفيذ UPDATE tasks SET title = "Hacked" WHERE id = 10 AND user_id = 99 على مهمة يملكها المستخدم 42؟

لأن الشرط المزدوج لا يطابق أي سجل، لا يتم تعديل أي شيء وترجع الدالة 0.