التحقق من بيانات الطلب ورموز الاستجابة
خط الدفاع الحقيقي: تحقّق من كل ما يصل، وأجب بالرمز الصحيح. ⏱ نحو 35 دقيقة.
- تشرح لماذا التحقق في الخادم إلزامي.
- تتحقق من الحقول بخطواتها: الوجود والتنظيف والإلزام والشكل والحدود.
- ترد برمز الحالة المناسب: 200 و201 و405 و422.
- تعيد عرض النموذج بقيم المستخدم، وتطبّق نمط
POSTثم التحويل.
١لا تثق بأي شيء يصل من الطلب
النموذج في المتصفح قد يكون فيه required و type="email"، فهل يكفي ذلك؟ لا. التحقق في المتصفح لراحة المستخدم فقط، لأن أي شخص يستطيع إرسال طلب مباشرة للخادم متجاوزًا النموذج كله: بأداة مثل Postman، أو من تبويب «📮 طلب يدوي» في مختبرنا.
التحقق في المتصفح (131) | التحقق في الخادم | |
|---|---|---|
| هدفه | تجربة استخدام أفضل وتنبيه فوري | حماية البيانات ومنطق التطبيق |
| هل يمكن تجاوزه؟ | نعم، بسهولة | لا: كل الطلبات تمر عليه |
| هل يكفي وحده؟ | لا | نعم، وهو إلزامي |
والقاعدة: تحقّق من كل حقل على الخادم، كأن النموذج غير موجود أصلًا.
٢خطوات التحقق
- الوجود: هل وصل الحقل؟
$_POST['x'] ?? '' - التنظيف: احذف المسافات الزائدة بـ
trim. - الإلزام: هل هو فارغ؟
$x === '' - الشكل والنوع: بريد صحيح؟ عدد؟ تاريخ؟ بـ
filter_var. - الحدود: الطول بـ
mb_strlen، والمدى (العمر بين 16 و60)، والقيم المسموحة بـin_array. - الرد: إن وُجدت أخطاء اجمعها كلها وأعدها برمز مناسب، وإلا فنفّذ العملية.
الدالة filter_var تتحقق من الشكل، وتعيد القيمة الصحيحة أو false:
<?phpvar_dump(filter_var("saud@tvtc.gov.sa", FILTER_VALIDATE_EMAIL));var_dump(filter_var("saud@", FILTER_VALIDATE_EMAIL));var_dump(filter_var("25", FILTER_VALIDATE_INT));var_dump(filter_var("25abc", FILTER_VALIDATE_INT));var_dump(filter_var("70", FILTER_VALIDATE_INT, ['options' => ['min_range' => 16, 'max_range' => 60]]));var_dump(in_array("كبير", ["صغير", "وسط", "كبير"]));
٣اجمع الأخطاء وردّ بالرمز المناسب
لا تتوقف عند أول خطأ؛ اجمعها كلها في مصفوفة، ليصلح المستخدم كل شيء مرة واحدة. ثم اختر رمز الحالة الذي يصف النتيجة:
| الرمز | متى؟ |
|---|---|
200 OK | نجح الطلب (عرض أو تعديل). |
201 Created | نجح وأُنشئ عنصر جديد (تسجيل، إضافة). |
400 Bad Request | الطلب نفسه مشوّه أو ناقص بشكل لا يُفهم. |
422 Unprocessable Content | الطلب مفهوم لكن البيانات لم تجتز التحقق. |
405 Method Not Allowed | طريقة غير مقبولة، مثل GET على ملف يقبل POST فقط. |
<?php$errors = [];$name = trim($_POST['name'] ?? '');$email = trim($_POST['email'] ?? '');$age = $_POST['age'] ?? ''; if ($name === '') { $errors[] = 'الاسم مطلوب.';} elseif (mb_strlen($name) < 3) { $errors[] = 'الاسم قصير جدًّا.';}if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { $errors[] = 'البريد الإلكتروني غير صحيح.';}if (filter_var($age, FILTER_VALIDATE_INT, ['options' => ['min_range' => 16, 'max_range' => 60]]) === false) { $errors[] = 'العمر يجب أن يكون عددًا من 16 إلى 60.';} if ($errors) { http_response_code(422); echo "<ul>"; foreach ($errors as $e) echo "<li>$e</li>"; echo "</ul>";} else { http_response_code(201); echo "<p>✓ تم تسجيل " . htmlspecialchars($name) . "</p>";}
الطلب الأول فيه ثلاثة أخطاء فعاد بها كلها مع 422، والثاني سليم فعاد بـ 201. جرّب في المختبر إرسال طلب بلا أي حقول.
٤النموذج اللاصق: لا تجعل المستخدم يكتب من جديد
إن أخطأ المستخدم في حقل واحد ثم عاد له النموذج فارغًا، سيغضب. أعد عرض النموذج بالقيم التي كتبها مع رسالة بجانب الحقل الخاطئ. ولاحظ: القيمة المعادة تمر على htmlspecialchars لأنها من المستخدم:
exit توقف تنفيذ الملف فورًا، فلا يُطبع النموذج بعد رسالة النجاح.
٥بعد POST الناجح: حوّل ولا تعرض
مشكلة: بعد إرسال طلب شراء بنجاح، يضغط المستخدم «تحديث» فيرسل المتصفح الطلب نفسه مرة أخرى… فيتكرر الشراء! الحل نمط مشهور اسمه POST ثم تحويل ثم GET (PRG): بعد نجاح POST لا تعرض صفحة، بل أرسل ترويسة Location برمز 303، فيطلب المتصفح صفحة التأكيد بـ GET:
<?phpif ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); exit('هذه الصفحة تقبل POST فقط');}$item = $_POST['item'] ?? '';// … هنا نحفظ الطلب …header('Location: thanks.php?item=' . urlencode($item), true, 303);exit;
<?php$item = htmlspecialchars($_GET['item'] ?? '');echo "<h2>شكرًا! استلمنا طلبك: $item</h2>";
الطلب الأول ردّ بـ 303 وفيه Location، فطلب المتصفح thanks.php (الطلب الثاني). والآن «تحديث» الصفحة يعيد GET لـ thanks.php فقط. والطلب الثالث يبيّن 405 حين تُفتح order.php مباشرة.
توقّع وتحقّق
التحقق في المتصفح وحده:
بيانات مفهومة لكنها لم تجتز التحقق. الرمز الأنسب:
Unprocessable Content.filter_var("abc", FILTER_VALIDATE_INT) تعيد:
الهدف من نمط PRG:
POST ثم Redirect ثم GET.الخلاصة
- التحقق في المتصفح للراحة، وفي الخادم للحماية؛ والثاني إلزامي دائمًا.
- ?? ثم
trimثم الإلزام ثمfilter_varثم الحدود ثم الرد. - اجمع الأخطاء كلها في مصفوفة، وردّ بـ 422 إن وُجدت.
- 201 للإنشاء، و405 للطريقة غير المسموحة، و
http_response_code()لضبط الرمز. - بعد
POSTالناجح:header Locationمع 303 ثمexit(نمطPRG).
تحدٍّ برمجي: التحقق من طلب حجز قاعة
متوسطنظام حجز قاعات التدريب يستقبل POST فيه: room (رقم القاعة)، و hours (عدد الساعات)، و email. اكتب التحقق.
- القاعات المسموحة: 101 و 102 و 201 فقط. وإلا فالخطأ:
<li>القاعة غير موجودة</li> - الساعات عدد صحيح من 1 إلى 4. وإلا:
<li>الساعات من 1 إلى 4</li> - البريد صحيح الشكل. وإلا:
<li>البريد غير صحيح</li> - إن وُجدت أخطاء: رمز 422 وكل الأخطاء داخل
<ul>. وإن صح كل شيء: رمز 201 والنص<p>تم حجز القاعة 101 لمدة 2 ساعات</p> - إن لم يكن الطلب POST: رمز 405.
👀 اعرض الاستجابة المطلوبة
POST /book.php(room=101&hours=2&email=a@tvtc.sa)POST /book.php(room=999&hours=9&email=bad)POST /book.php(room=201&hours=0&email=b@tvtc.sa)GET /book.phpPOST /book.php
- الكود يعمل بلا أخطاء أو تحذيرات
- طلب سليم ← 201
- طلب سليم ← تم حجز القاعة 101 لمدة 2 ساعات
- ثلاثة أخطاء ← 422
- ثلاثة أخطاء ← تظهر الرسائل الثلاث
hours=0← الساعات من 1 إلى 4 فقطhours=0← لا تظهر أخطاء القاعة والبريد- GET ← 405
- POST فارغ ← 422 بلا تحذيرات
💡 تلميح 1
$errors = []; ثم if ($errors) بعد كل الفحوص.💡 تلميح 2
hours=0: استخدم === false مع filter_var.💡 تلميح 3
حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
<?phpif ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); exit('POST فقط');}$room = $_POST['room'] ?? '';$hours = $_POST['hours'] ?? '';$email = trim($_POST['email'] ?? '');$errors = []; if (!in_array($room, ['101', '102', '201'], true)) { $errors[] = 'القاعة غير موجودة';}if (filter_var($hours, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 4]]) === false) { $errors[] = 'الساعات من 1 إلى 4';}if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { $errors[] = 'البريد غير صحيح';} if ($errors) { http_response_code(422); echo "<ul>"; foreach ($errors as $e) { echo "<li>$e</li>"; } echo "</ul>";} else { http_response_code(201); echo "<p>تم حجز القاعة $room لمدة $hours ساعات</p>";}