في هذا الدرس 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() وسم السكريبت الخبيث.
<?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
$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";
عند عرض نموذج التعديل، املأ خاصية 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
$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
$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 / List | GET /tasks | Auth guard + WHERE user_id = :u + LIMIT/OFFSET + htmlspecialchars() |
| Create | POST /tasks | Auth guard + CSRF check + validate + INSERT + 303 PRG redirect |
| Update | POST /tasks/update | Auth guard + CSRF check + WHERE id = :id AND user_id = :u + 303 PRG |
| Delete | POST /tasks/delete | Auth guard + CSRF check + WHERE id = :id AND user_id = :u + 303 PRG |
<?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
راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.
- 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
راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.
- 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 ويرسلها إلى متصفح العميل بعد التنفيذ.
- للمراجعة
لا توجد بطاقات تطابق البحث والتصفية.
اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.
مهامك قبل الاختبار
خطوة صغيرة كل مرة. وإنجاز تقدر ترجع له.
-
أنشئ ملف PHP محلياً ونفّذ المثال البرمجي المشروح في قسم عرض السجلات مع البحث الفوري وتقسيم الصفحات للتحقق من المخرجات.
-
بدّل بين الحالات الثلاث في المحاكي التفاعلي ضمن قسم إضافة سجل جديد مع التحقق وحماية CSRF ونمط PRG وقارن مخرجات التنفيذ.
تأشير المهمة إقرار منك بإتمامها، وليس تصحيحًا آليًا للكود. يُحفظ تقدمك في حسابك.
اختبر فهمك.
ووصلها للإتقان.
كل سؤال خطوة لقدّام. اختار، اكتب كود، وشوف نتيجتك فورًا.
أكمل المهام، واجمع كل الإجابات الصحيحة. أقل من 100%؟ راجع وأعد المحاولة.
الأسئلة متاحة للاطلاع. سجّل الدخول للتفاعل وحفظ تقدمك.
لمحة عن التحدي8 أسئلة تنتظرك +
ما هي ثغرة الوصول المباشر غير الآمن للكائنات (IDOR) في لوحة تحكم CRUD؟
تحدث ثغرة IDOR عندما يفحص الاستعلام WHERE id = :id فقط دون التحقق من ملكية السجل عبر AND user_id = :session_user_id.
كيف تمنع ثغرة IDOR عند تحديث أو حذف مهمة في استعلام SQL؟
تقييد الاستعلام بمعرف المستخدم الموثق في الجلسة يضمن عدم تأثر أي سجل لا يملكه.
لماذا يجب تنفيذ عمليات الحذف عبر نموذج POST محمي بتوكن CSRF بدلاً من رابط GET مثل /tasks/delete?id=10؟
طلبات GET مخصصة للقراءة فقط، أما العمليات التدميرية كالحذف فتتطلب POST مع حماية CSRF.
كيف تحسب قيمة الإزاحة OFFSET في استعلام تقسيم الصفحات عندما يكون لديك رقم الصفحة $page (يبدأ من 1) وعدد العناصر $perPage؟
في الصفحة الأولى تكون الإزاحة (1 - 1) * 10 = 0، وفي الصفحة الثانية تكون 10.
لماذا نكتب $page = max(1, (int) ($_GET["page"] ?? 1)) قبل حساب إزاحة الصفحات OFFSET؟
الإزاحة السالبة تسبب خطأ في استعلام قاعدة البيانات.
عند طباعة عناوين المهام التي أدخلها المستخدم داخل جدول HTML أو داخل خاصية value لحقل إدخال، أي دالة يجب أن تغلف المخرجات؟
ترميز محارف HTML الخاصة يمنع ثغرات XSS المخزنة في الجداول والحقول.
من أين يجب أن يحصل متحكم الإضافة أو التعديل على معرف المستخدم الحالي user_id؟
بيانات جلسة الخادم محفوظة على السيرفر ولا يمكن للمتصفح التلاعب بها.
ماذا ترجع $stmt->rowCount() عندما يحاول مهاجم (المستخدم 99) تنفيذ UPDATE tasks SET title = "Hacked" WHERE id = 10 AND user_id = 99 على مهمة يملكها المستخدم 42؟
لأن الشرط المزدوج لا يطابق أي سجل، لا يتم تعديل أي شيء وترجع الدالة 0.