في هذا الدرس 6تصفّح المواضيع
يربط نظام البحث الفوري المباشر (Autocomplete) حقل البحث في الواجهة الأمامية بنقطة نهاية PHP متصلة بقاعدة البيانات عبر PDO لإرجاع النتائج المطابقة أثناء كتابة المستخدم.
- يستمع المتصفح لحدث الكتابة
inputفي حقل البحث وينتظر توقفاً قصيراً قبل استدعاءfetch("/api/search.php?q=..."). - يتحقق ملف PHP من طول نص البحث، وينفذ استعلاماً محضراً
WHERE title LIKE :q LIMIT 8عبر PDO، ثم يرجع النتائج بصيغة JSON. - تقوم الواجهة الأمامية بتفريغ القائمة المنسدلة وعرض العناصر المطابقة بأمان.
يمنع فرض الشرط mb_strlen($q) >= 2 في المتصفح والخادم معاً استعلامات البحث المكلفة المكونة من حرف واحد فقط.
<?php
$q = trim((string) ($_GET['q'] ?? 'pdo'));
if (mb_strlen($q) < 2) {
echo json_encode(['ok' => true, 'count' => 0, 'results' => []]) . "\n";
return;
}
echo json_encode([
'ok' => true,
'query' => $q,
'pattern' => '%' . $q . '%',
]) . "\n";
بدون تقنية التأخير الذكي (Debounce)، فإن كتابة كلمة من 10 أحرف تطلق 10 طلبات HTTP منفصلة إلى خادم PHP خلال ثانية واحدة، مما يرهق قاعدة البيانات ويسبب تداخل النتائج.
- تعتمد تقنية Debounce على إعادة ضبط مؤقت زمني (عادةً من
250إلى300مللي ثانية) مع كل ضغطة زر، فلا يُرسل الطلب إلا بعد توقف المستخدم عن الكتابة. - اربط تقنية Debounce بكائن
AbortControllerفي JavaScript لإلغاء أي طلب سابق لم يكتمل بعد عند بدء بحث جديد. - حتى مع وجود Debounce في الواجهة، احرص دائماً على إبقاء استعلام PHP سريعاً ومقيداً بسقف
LIMIT.
يقلص استخدام Debounce عدد الطلبات من 14 ضغطة مفتاح متتالية إلى طلبين فقط لخادم PHP، مما يوفر 86% من الحمل على قاعدة البيانات.
<?php
// Comparing server load with and without 280ms input debouncing
$typedWord = "authentication";
$rawKeystrokes = strlen($typedWord);
$debouncedRequests = 2; // User paused once mid-word and once at the end
$savedPercent = round((1 - ($debouncedRequests / $rawKeystrokes)) * 100);
echo "Raw requests : $rawKeystrokes\n";
echo "Debounced requests : $debouncedRequests ($savedPercent% load reduction)\n";
داخل ملف البحث في PHP، ادمج علامتي النسبة المئوية % داخل قيمة المتغير في PHP ثم اربطه كمعامل محضر في PDO، ولا تدمج $_GET["q"] داخل نص SQL أبداً!
- اكتب المعامل المحضر في SQL بالصورة
WHERE title LIKE :term، وليس داخل علامات تنصيص'%:term%'. - هرّب الرمزين
%و_داخل كلمة البحث عبرaddcslashes($q, "%_\\")لمنع المستخدم من إدخال أنماط بحث شاملة تبطئ الخادم. - اجلب الأعمدة اللازمة فقط للقائمة المنسدلة (مثل
id, title, slug) بدلاً من جلب كامل الصفوف بـSELECT *.
يحضر المثال استعلام LIKE :q عبر PDO، ويربط القيمة "%Password%" بأمان، ثم يعيد الصف المطابق بصيغة JSON.
<?php
$pdo = new PDO('sqlite::memory:');
$pdo->exec("CREATE TABLE lessons (id INT, title TEXT, slug TEXT)");
$pdo->exec("INSERT INTO lessons VALUES (85, 'PHP Password Hashing', 'php-password-hashing')");
$pdo->exec("INSERT INTO lessons VALUES (86, 'PHP CSRF Protection', 'php-csrf-protection')");
$rawQ = "Password";
$escaped = addcslashes($rawQ, '%_\\');
$stmt = $pdo->prepare("SELECT id, title, slug FROM lessons WHERE title LIKE :q LIMIT 8");
$stmt->execute([':q' => '%' . $escaped . '%']);
echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC)) . "\n";
لا ينبغي لقائمة الإكمال التلقائي أن تُرجع آلاف السجلات؛ لذا قيّد استعلام البحث الفوري دائماً بـ LIMIT 8 أو LIMIT 10 ونسّق عناصر المصفوفة.
- قيّد الحد الأقصى لطول نص البحث (مثلاً
mb_substr($q, 0, 60)) قبل تمريره للاستعلام. - حوّل صفوف قاعدة البيانات إلى مصفوفة نظيفة تضم المفاتيح المطلوبة فقط للعرض مثل
idوtitleوurl. - أرفق مفتاح العدد
countومصفوفة النتائجresultsفي كائن JSON النهائي.
تحول دالة array_map() صفوف قاعدة البيانات الخام إلى كائنات جاهزة للاستخدام المباشر في الواجهة مع بناء مسار الرابط url.
<?php
$rows = [
['id' => 89, 'title' => 'PHP AJAX Intro', 'slug' => 'php-ajax-introduction'],
['id' => 91, 'title' => 'AJAX Live Search', 'slug' => 'php-ajax-live-search'],
];
$formatted = array_map(fn(array $r): array => [
'id' => (int) $r['id'],
'title' => $r['title'],
'url' => '/tutorials/php/' . $r['slug'],
], $rows);
echo json_encode(['ok' => true, 'count' => count($formatted), 'results' => $formatted]) . "\n";
عند عرض نتائج البحث الفوري في المتصفح، أنشئ عناصر القائمة عبر document.createElement() وأسند العناوين عبر خاصية textContent الآمنة.
- لا تقم أبداً ببناء عناصر القائمة المنسدلة بدمج النصوص مباشرة داخل
resultsBox.innerHTML += "...". - استخدم
document.createElement("a")، واضبط الرابطa.href = item.url، والعنوانa.textContent = item.title. - اعرض رسالة واضحة للمستخدم عند عدم وجود نتائج متطابقة (عندما يكون
data.results.length === 0).
يضمن تنظيف المعرف النصي على الخادم أن يكون item.url مساراً نسبياً آمناً دائماً، بينما تتكفل textContent في المتصفح بعرض العنوان بأمان.
<?php
// Server-side validation of generated result URLs before JSON output
function buildSafeSearchResult(int $id, string $title, string $slug): array {
$cleanSlug = preg_replace('/[^a-z0-9-]/', '', strtolower($slug));
return [
'id' => $id,
'title' => $title,
'url' => '/tutorials/php/' . $cleanSlug,
];
}
$item = buildSafeSearchResult(91, 'Live Search <PDO>', 'php-ajax-live-search');
echo json_encode($item) . "\n";
اجمع بين ضوابط الواجهة الأمامية والخادم التالية لضمان بقاء نظام البحث الفوري سريعاً وآمناً تماماً ضد الهجمات.
- طبّق تأخير الكتابة Debounce (من 250 إلى 300 مللي ثانية) وألغِ الطلبات السابقة عبر
AbortController. - افرض حداً أدنى لطول النص (حرفان فأكثر) وحداً أقصى (60 محرفاً) داخل كود PHP.
- استخدم استعلامات PDO المحضرة مع تهريب رموز LIKE وتقييد النتائج بـ
LIMIT 8.
تزيل دالة normalizeSearchTerm() المسافات الزائدة، وتفحص حدود الطول بترميز UTF-8، وتهرّب الشرطة السفلية، وتحيط النص بعلامتي %.
| معيار الأداء / الأمان | مكان التطبيق | الأهمية العملية |
|---|---|---|
| Input Debounce (280ms) | Frontend JS | Cuts HTTP request volume by 80%+ |
AbortController | Frontend JS | Prevents stale responses overwriting newer ones |
mb_strlen($q) >= 2 | Both JS & PHP | Blocks expensive 1-letter table scans |
PDO Prepared LIKE :q | Backend PHP | Eliminates SQL Injection completely |
LIMIT 8 + Specific Columns | Backend SQL | Caps memory & network payload size |
textContent Rendering | Frontend DOM | Eliminates DOM-based XSS in dropdown |
<?php
function normalizeSearchTerm(string $raw): ?string {
$trimmed = trim($raw);
$len = mb_strlen($trimmed, 'UTF-8');
if ($len < 2 || $len > 60) {
return null;
}
return '%' . addcslashes($trimmed, '%_\\') . '%';
}
echo (normalizeSearchTerm(" pdo_8 ") ?? "SKIP") . "\n";
ملخص الدرس
حصاد الدرسمراجعة مركّزة لأهم المفاهيم والقواعد الهندسية التي أتقنتها في هذا الدرس قبل التطبيق العملي
ماذا تعلّمت في هذا الدرس؟
اجمع بين ضوابط الواجهة الأمامية والخادم التالية لضمان بقاء نظام البحث الفوري سريعاً وآمناً تماماً ضد الهجمات. طبّق تأخير الكتابة Debounce (من 250 إلى 300 مللي ثانية) وألغِ الطلبات السابقة عبر AbortController. افرض حداً أدنى لطول النص (حرفان فأكثر) وحداً أقصى (60 محرفاً) داخل كود PHP. استخدم استعلامات PDO المحضرة مع تهريب رموز LIKE وتقييد النتائج بـ LIMIT 8.
سجّل الدخول لحفظ تقدمك
تابع إنجاز المواضيع والمصطلحات والمهام، وكمّل من آخر خطوة على أي جهاز.
المصطلحات4
راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.
- 01 AJAX Live Search البحث الفوري Live Search
- ابنِ محرك بحث فوري الإكمال التلقائي باستخدام PHP 8 واستعلامات PDO المحضرة بـ LIKE واستجابات JSON وتأخير الإدخال Debounce.
- للمراجعة
- 02 Architecture of AJAX Live Search معمارية نظام البحث الفوري المباشر Live Search
- المفهوم الأساسي لقسم معمارية نظام البحث الفوري المباشر Live Search في لغة PHP الحديثة.
- للمراجعة
- 03 Debouncing Keystrokes on the Frontend تقليل طلبات الخادم بتقنية Debounce عند الكتابة
- الصياغة البرمجية والتطبيق العملي المباشر لقسم تقليل طلبات الخادم بتقنية Debounce عند الكتابة.
- للمراجعة
- 04 Building the PDO LIKE Search Endpoint بناء نقطة نهاية البحث الآمنة بـ PDO و LIKE
- قواعد التحقق وأفضل الممارسات القياسية الخاصة بقسم بناء نقطة نهاية البحث الآمنة بـ PDO و LIKE.
- للمراجعة
لا توجد بطاقات تطابق البحث والتصفية.
اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.
الاختصارات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 محلياً ونفّذ المثال البرمجي المشروح في قسم معمارية نظام البحث الفوري المباشر Live Search للتحقق من المخرجات.
-
بدّل بين الحالات الثلاث في المحاكي التفاعلي ضمن قسم تقليل طلبات الخادم بتقنية Debounce عند الكتابة وقارن مخرجات التنفيذ.
تأشير المهمة إقرار منك بإتمامها، وليس تصحيحًا آليًا للكود. يُحفظ تقدمك في حسابك.
اختبر فهمك.
ووصلها للإتقان.
كل سؤال خطوة لقدّام. اختار، اكتب كود، وشوف نتيجتك فورًا.
أكمل المهام، واجمع كل الإجابات الصحيحة. أقل من 100%؟ راجع وأعد المحاولة.
الأسئلة متاحة للاطلاع. سجّل الدخول للتفاعل وحفظ تقدمك.
لمحة عن التحدي8 أسئلة تنتظرك +
لماذا تُعد تقنية تأخير الإدخال (Debouncing) ضرورية في الواجهة الأمامية لحقل البحث الفوري AJAX؟
تمنع إطلاق استعلام قاعدة بيانات منفصل مع كل ضغطة مفتاح.
كيف يجب ربط معامل البحث الجزئي LIKE المحتوي على علامة % في استعلام PDO محضر؟
توضع علامتا % داخل قيمة المتغير في PHP عند الربط، وليس حول اسم المعامل في نص SQL.
لماذا يجب أن يفحص ملف البحث الفوري في PHP الشرط if (mb_strlen($q, "UTF-8") < 2) قبل استعلام قاعدة البيانات؟
البحث بحرف واحد يطابق معظم السجلات ويستهلك موارد قاعدة البيانات دون فائدة.
ما فائدة استخدام addcslashes($q, "%_\\") قبل إحاطة كلمة بحث المستخدم بعلامتي "%"؟
في معامل LIKE، يطابق الرمز % أي نص ويطابق _ أي حرف مفرد ما لم يتم تهريبهما.
لماذا يجب أن يتضمن استعلام SQL الخاص بالإكمال التلقائي دائماً شرطاً مثل LIMIT 8؟
يحتاج المستخدم لرؤية أول 5 إلى 10 اقتراحات فقط، وإرجاع آلاف الصفوف يبطئ النظام.
ما المشكلة التي يحلها كائن AbortController في JavaScript داخل نظام البحث الفوري؟
إلغاء الطلبات المتجاوزة يمنع تعارض التوقيت بين استجابات الشبكة.
لماذا يجب استخدام mb_strlen($q, "UTF-8") بدلاً من strlen($q) عند فحص طول كلمة بحث باللغة العربية؟
الحرف العربي الواحد يشغل بايتين في UTF-8، فترجع strlen("م") الرقم 2 بينما ترجع mb_strlen الرقم 1 بدقة.
كيف يجب أن يعرض المتصفح كل عنصر مطابق عائد من نقطة نهاية البحث الفوري بصيغة JSON؟
استخدام createElement() مع textContent يمنع ثغرات XSS حتى لو احتوى العنوان على أقواس زاوية.