اختبار النشر والتحقق منه
تأكد أن تطبيقك يعمل فعلًا للناس، لا لجهازك فقط. ⏱ نحو 30 دقيقة.
- تشرح لماذا يلزم اختبار التطبيق على بيئة الاستضافة نفسها.
- تكتب خطة اختبار نشر تشمل الوصول والوظائف والأخطاء والأمان والأجهزة والأداء.
- تبني اختبار دخان آليًّا يفحص الروابط ورموز حالتها.
- تقيس زمن الاستجابة وتستفيد من السجلات والمراقبة.
١«يعمل على جهازي» لا يكفي
تطبيق يعمل على XAMPP قد يفشل على الخادم لأسباب كثيرة: إصدار PHP مختلف، أو إضافة ناقصة، أو اسم ملف بأحرف كبيرة (Linux يفرّق بين Login.php و login.php بخلاف Windows)، أو صلاحية مجلد، أو قاعدة لم تُستورد. لذلك الخطوة الأخيرة في النشر: اختبار شامل على الخادم نفسه، والتحقق من الوصول من أجهزة ومتصفحات مختلفة.
٢خطة اختبار النشر
| المجال | ماذا تختبر؟ | كيف؟ |
|---|---|---|
| الوصول | الرابط يفتح، و http يحوّل إلى https، والشهادة صالحة | المتصفح، وأيقونة القفل |
| الوظائف | كل وظيفة في جدول التحليل (الدرس 1): الدخول، والإضافة، والتعديل، والحذف، والبحث | سيناريو مكتوب خطوة بخطوة |
| الأخطاء | صفحة غير موجودة ← 404، والخطأ لا يكشف مسارات، والسجلات تعمل | روابط خاطئة متعمدة |
| الأمان | لا phpinfo ولا .sql ولا .git منشورة، والصفحات المحمية تحوّل للدخول | طلب هذه الروابط مباشرة |
| الأجهزة والمتصفحات | جوال وحاسب، و Chrome و Safari و Firefox و Edge | أجهزة حقيقية، وأداة الجوال في F12 |
| الأداء | زمن الاستجابة، وحجم الصفحة | تبويب Network، وأداة Lighthouse في Chrome |
اكتب الاختبارات قائمةً تُعاد بعد كل نشر، لا تجربة عشوائية. فالنشر الثاني والثالث قد يكسر ما كان يعمل.
٣اختبار دخان آلي
اختبار الدخان Smoke test: فحص سريع لأهم الصفحات بعد كل نشر، يكشف إن «احترق» شيء أساسي. نكتبه في PHP بدالة http_get من الدرس 14، فيطلب كل رابط ويقارن رمز الحالة. في المختبر ردود «الموقع المنشور» محاكاة؛ وعلى جهازك يعمل الكود نفسه على موقعك الحقيقي بنسخة cURL:
<?phprequire __DIR__ . '/http.php';$base = 'https://app.tvtc-demo.sa'; $tests = [ ['/', 200, 'إدارة المستخدمين'], ['/login.php', 200, '<form'], ['/api/users.php', 401, null], ['/phpinfo.php', 404, null], ['/backup.sql', 404, null],]; $passed = 0;foreach ($tests as [$path, $status, $text]) { [$got, $body] = http_get($base . $path); $ok = $got === $status && ($text === null || str_contains($body, $text)); if ($ok) $passed++; echo ($ok ? '✓' : '✗') . " $path ($got)\n";}echo "نجح $passed من " . count($tests);
<?php// http.php: نسخة المختبر من http_get// المختبر بلا إنترنت، فنحاكي الخدمات بملفات JSON في مجلد mock// وعلى XAMPP استبدل هذا الملف بنسخة cURL (في الدرس) دون تغيير أي شيء آخرfunction http_get(string $url): array{ $name = trim(preg_replace('/[^a-z0-9]+/i', '_', $url), '_'); // اسم ملف من الرابط $file = __DIR__ . '/mock/' . $name . '.json'; if (!file_exists($file)) { return [0, 'لا اتصال']; } $r = json_decode(file_get_contents($file), true); // {"status": 200, "body": "…"} return [$r['status'], $r['body']];}
{"status": 200, "body": "<h1>إدارة المستخدمين</h1><a href=\"login.php\">دخول</a>"}{"status": 200, "body": "<form method=\"post\"><input name=\"username\"></form>"}{"status": 401, "body": "{\"error\":\"رمز الدخول مفقود\"}"}{"status": 200, "body": "<title>PHP 8.4 - phpinfo()</title>"}{"status": 404, "body": "Not Found"}اختبار phpinfo.php فشل: الصفحة ما زالت منشورة! هذا بالضبط ما يُفترض أن يكتشفه الاختبار قبل أن يكتشفه مهاجم.
٤قياس زمن الاستجابة
المستخدم يغادر إن تأخرت الصفحة. قِس الزمن على الخادم نفسه لتعرف أين يذهب الوقت:
<?php$t0 = microtime(true); $pdo = new PDO('sqlite::memory:');$pdo->exec('CREATE TABLE t (n INTEGER)');$s = $pdo->prepare('INSERT INTO t VALUES (?)');$pdo->beginTransaction();for ($i = 0; $i < 2000; $i++) $s->execute([$i]);$pdo->commit();$t1 = microtime(true); $sum = $pdo->query('SELECT SUM(n) FROM t')->fetchColumn();$t2 = microtime(true); header('Server-Timing: db_insert;dur=' . round(($t1 - $t0) * 1000) . ', db_query;dur=' . round(($t2 - $t1) * 1000));echo "المجموع: $sum";
الترويسة Server-Timing تظهر في أدوات المطوّر (F12 ← Network ← Timing)، فترى زمن كل جزء دون أن تكشفه في الصفحة. والأرقام تختلف من جهاز لآخر؛ المهم المقارنة قبل التحسين وبعده.
٥السجلات والمراقبة
- سجل أخطاء
PHP: افتحه بعد النشر وبعد الاختبارات. خطأ يتكرر في السجل مشكلة لم يبلّغ عنها أحد بعد. - سجل وصول
Apache: كل طلب ورمزه؛ كثرة 404 لرابط ما تعني رابطًا مكسورًا، وكثرة 401 على صفحة الدخول قد تعني محاولة تخمين. - صفحة الصحة
health.php(الدرس 24) يطلبها نظام المراقبة دوريًّا. - النسخ الاحتياطي: جرّب استرجاع نسخة فعلًا مرة واحدة على الأقل؛ النسخة التي لم تُجرَّب قد لا تعمل.
توقّع وتحقّق
اختبار سريع لأهم الصفحات بعد كل نشر يسمى:
Smoke test.الملف Login.php يعمل على Windows ويفشل على Linux عند طلب login.php لأن:
أداة Chrome لقياس الأداء وإمكانية الوصول:
F12 ← Lighthouse.ما الذي يجب أن يرد بـ 404 على الموقع المنشور؟
الخلاصة
- ما يعمل على
XAMPPقد يفشل على الخادم: الإصدار، والإضافات، وحالة الأحرف، والصلاحيات. - خطة اختبار مكتوبة تُعاد بعد كل نشر، من أجهزة وشبكات ومتصفحات مختلفة.
- اختبار الدخان: اطلب أهم الروابط وقارن رمز الحالة والنص.
microtimeوServer-TimingوLighthouseلقياس الأداء.- السجلات وصفحة الصحة والنسخ الاحتياطي المجرَّب جزء من النشر.
تحدٍّ برمجي: اختبار الدخان
متوسطأكمل smoke.php: يطلب كل رابط في القائمة من الموقع المنشور (المحاكى) ويطبع تقريرًا.
- لكل اختبار سطر:
✓ /login.php (200)إن طابق رمز الحالة (والنص المطلوب إن وُجد في الجسم)، وإلا✗ /phpinfo.php (200)برمز الحالة الفعلي. - في الآخر:
نجح 4 من 5. - استخدم http_get، و str_contains لفحص النص.
👀 اعرض الاستجابة المطلوبة
GET /smoke.php
- الكود يعمل بلا أخطاء أو تحذيرات
- التقرير مطابق تمامًا (خمسة أسطر ثم الملخص)
- تستخدم http_get و str_contains
💡 تلميح 1
foreach ($tests as [$path, $status, $text]) يفك كل اختبار إلى ثلاثة متغيرات.💡 تلميح 2
$got === $status && ($text === null || str_contains($body, $text))حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
<?phprequire __DIR__ . '/http.php';$base = 'https://app.tvtc-demo.sa'; $tests = [ ['/', 200, 'إدارة المستخدمين'], ['/login.php', 200, '<form'], ['/api/users.php', 401, null], ['/phpinfo.php', 404, null], ['/backup.sql', 404, null],]; $passed = 0;foreach ($tests as [$path, $status, $text]) { [$got, $body] = http_get($base . $path); $ok = $got === $status && ($text === null || str_contains($body, $text)); if ($ok) $passed++; echo ($ok ? '✓' : '✗') . " $path ($got)\n";}echo "نجح $passed من " . count($tests);
<?php// http.php: نسخة المختبر من http_get// المختبر بلا إنترنت، فنحاكي الخدمات بملفات JSON في مجلد mock// وعلى XAMPP استبدل هذا الملف بنسخة cURL (في الدرس) دون تغيير أي شيء آخرfunction http_get(string $url): array{ $name = trim(preg_replace('/[^a-z0-9]+/i', '_', $url), '_'); // اسم ملف من الرابط $file = __DIR__ . '/mock/' . $name . '.json'; if (!file_exists($file)) { return [0, 'لا اتصال']; } $r = json_decode(file_get_contents($file), true); // {"status": 200, "body": "…"} return [$r['status'], $r['body']];}
{"status": 200, "body": "<h1>إدارة المستخدمين</h1><a href=\"login.php\">دخول</a>"}{"status": 200, "body": "<form method=\"post\"><input name=\"username\"></form>"}{"status": 401, "body": "{\"error\":\"رمز الدخول مفقود\"}"}{"status": 200, "body": "<title>PHP 8.4 - phpinfo()</title>"}{"status": 404, "body": "Not Found"}