10. خوادم في PHP
نظرًا لأن البرامج PHP يمكن تنفيذها بواسطة خادم WEB، فإن مثل هذا البرنامج يصبح برنامج خادم قادرًا على خدمة عدة عملاء. من وجهة نظر العميل، فإن استدعاء خدمة WEB يعادل طلب URL لهذه الخدمة. يمكن كتابة العميل بأي لغة، لا سيما بلغة PHP. في هذه الحالة الأخيرة، يتم استخدام وظائف الشبكة التي رأيناها للتو. كما يتعين علينا أن نعرف كيفية "التواصل" مع خدمة WEB، أي فهم بروتوكول http للاتصال بين خادم الويب وعملائه. وهذا هو الهدف من البرامج التالية.
وقد سمح لنا عميل الويب الموصوف في الفقرة 9.2 باكتشاف جزء من بروتوكول HTTP.

في أبسط صورها، تتم عمليات التبادل بين العميل والخادم على النحو التالي:
- يقوم العميل بفتح اتصال مع المنفذ 80 لخادم الويب
- يُرسل طلبًا بشأن مستند ما
- يرسل خادم الويب المستند المطلوب ويغلق الاتصال
- يقوم العميل بدوره بإغلاق الاتصال
يمكن أن يكون العميل من أنواع متنوعة: نص بتنسيق HTML، أو صورة، أو مقطع فيديو، ... وقد يكون مستندًا موجودًا (مستند ثابت) أو مستندًا يتم إنشاؤه على الفور بواسطة برنامج نصي (مستند ديناميكي). في الحالة الأخيرة، نُطلق على ذلك اسم برمجة الويب. يمكن كتابة البرنامج النصي لتوليد المستندات ديناميكيًا بعدة لغات: PHP، Python، Perl، Java، Ruby، C#، VB.net، ...
نستخدم هنا PHP لإنشاء مستندات نصية ديناميكيًا.
![]() |
- في [1]، يقوم العميل بفتح اتصال مع الخادم، ويطلب برنامجًا نصيًّا PHP، ويرسل أو لا يرسل معلمات إلى هذا البرنامج النصي
- في [2]، يقوم خادم الويب بتشغيل البرنامج النصي PHP بواسطة مترجم PHP. ويقوم هذا البرنامج النصي بإنشاء مستند يتم إرساله إلى العميل [3]
- يقوم الخادم بإنهاء الاتصال. ويقوم العميل بالمثل.
يمكن لخادم الويب معالجة عدة عملاء في وقت واحد. مع حزمة البرامج WampServer، يكون خادم الويب هو خادم Apache، وهو خادم مفتوح المصدر من مؤسسة Apache (http://www.apache.org/). في التطبيقات التالية، يجب تشغيل WampServer. يؤدي ذلك إلى تنشيط ثلاثة برامج: خادم الويب Apache، و SGBD MySQL، ومترجم PHP.
سيتم كتابة البرامج النصية التي ينفذها خادم الويب باستخدام أداة Netbeans. وقد كتبنا حتى الآن برامج نصية PHP تُنفذ في سياق وحدة التحكم:
![]() |
يستخدم المستخدم وحدة التحكم لطلب تنفيذ البرنامج النصي PHP والحصول على نتائجه.
في تطبيقات العميل/الخادم التالية،
- يتم تنفيذ البرنامج النصي للعميل في سياق وحدة التحكم
- يتم تنفيذ البرنامج النصي الخاص بالخادم في سياق الويب
![]() |
لا يمكن أن يكون البرنامج النصي PHP الخاص بالخادم موجودًا في أي مكان في نظام الملفات. في الواقع، يبحث خادم الويب في المواقع المحددة في التكوين عن المستندات الثابتة والديناميكية المطلوبة منه. تؤدي الإعدادات الافتراضية لـ WampServer إلى البحث عن المستندات في المجلد <WampServer>/www، حيث يمثل <WampServer> مجلد تثبيت WampServer. وبالتالي، إذا طلب عميل الويب مستند D باستخدام URL [http://localhost/D]، فسيقوم خادم الويب بتقديم المستند D الموجود في المسار [<WampServer>/www/D].
في الأمثلة التالية، سنضع نصوص الخادم في المجلد [www/exemples-web]. إذا كان اسم نص الخادم هو S.php، فسيُطلب من خادم الويب باستخدام URL و [http://localhost/exemples-web/S.php]. سيتم عندئذ تقديم المستند [<WampServer>/www/exemples-web/S.php] إليه.
![]() |
لإنشاء برنامج نصي للخادم باستخدام NetBeans لـ ، سنتبع الخطوات التالية:
![]() |
- في [1]، نقوم بإنشاء مشروع جديد
- في [2]، نختار الفئة [PHP] والمشروع [PHP Application]
![]() |
- في [3]، نسمي المشروع
- في [4]، نختار مجلدًا للمشروع
- في [5]، نحدد أن البرنامج النصي يجب أن يتم تنفيذه بواسطة خادم ويب محلي (سيكون عنوان البرنامج النصي URL على الشكل http://localhost/...). وسيكون خادم الويب المحلي هو خادم Apache الخاص بـ WampServer.
- في [6]، نحدد عنوان المشروع. هنا، نقرر أن يتم طلب البرنامج النصي S.php الخاص بالمشروع مع URL و[http://localhost/exemples-web/S.php]. وبناءً على ما سبق، فإن مسار البرنامج النصي S.php في نظام الملفات سيكون [<WampServer>/www/exemples-web/S.php]. وهذا ما هو محدد في [7]. نطلب هنا أن يتم نسخ كل سكريبت S.php من المشروع إلى شجرة ملفات خادم الويب Apache.
- في [8]، المشروع الجديد.
لنكتب برنامجًا نصيًّا تجريبيًّا:
![]() |
- في [1]، نقوم بإنشاء أول نص برمجي PHP
- في [2]، نسميه
- في [3]، بعد إنشائه، نضع فيه المحتوى التالي
للمتابعة، يجب تشغيل WampServer.
![]() |
- في [4]، نقوم بتنفيذ البرنامج النصي للويب [exemple1.php]. سيقوم Netbeans بعد ذلك بتشغيل المتصفح الافتراضي للجهاز وسيطلب منه عرض URL [http://localhost/exemples-web/exemple1.php] [5]
- في [6]، يعرض المتصفح ما كتبه البرنامج النصي للخادم إلى العميل.
بعد ذلك، سنواجه نوعين من عملاء الويب:
- متصفح مثل المذكور أعلاه. وقد ذكرنا أن خادم الويب يرسل استجابة على النحو التالي: رؤوس HTTP، سطر فارغ، نص. لا يعرض المتصفح سوى texte.
- نص برمجي PHP الذي سيعرض له الرد بالكامل: رؤوس HTTP، سطر فارغ، نص.
فيما يلي،
- ستُكتب البرامج النصية الخاصة بالخادم على غرار [exemple1.php] المذكور أعلاه
- ستُكتب نصوص العميل مثل نصوص وحدة التحكم التي كتبناها حتى الآن.
10.1. تطبيق العميل/الخادم للتاريخ/الوقت
10.1.1. الخادم (web_01)
<?php
// الوقت: عدد المللي ثانية منذ 01/01/1970
// تنسيق عرض التاريخ والوقت
// d: اليوم برقمين
// m: الشهر برقمين
// y: السنة برقمين
// H: الساعة 0,23
// i: الدقائق
// s: الثواني
print date("d/m/y H:i:s",time());
بشكل أساسي، يقوم البرنامج النصي PHP أعلاه بكتابة الوقت الحالي على الشاشة. ومع ذلك، عند تنفيذه بواسطة خادم ويب، يتم إعادة توجيه الدفق رقم 1 الذي يرتبط عادةً بالشاشة إلى الاتصال الذي يربط الخادم بعميله. لذلك، في سياق الويب، يرسل البرنامج النصي أعلاه الوقت الحالي في شكل نص إلى العميل.
لنقم بتشغيل هذا البرنامج النصي داخل NetBeans:
![]() |
- في [1]، يتم تنفيذ البرنامج النصي. ثم يتم تشغيل متصفح ويب.
- في [2]، يتم طلب URL من قبل متصفح الويب
- في [3]، النص المرسل بواسطة البرنامج النصي للخادم
يستخدم متصفح العميل بروتوكول HTTP للتواصل مع خادم الويب. وقد سبق أن وصفنا شكل هذا البروتوكول.
يرسل العميل أسطر نصية يمكن تقسيمها إلى ثلاثة أجزاء: رؤوس HTTP، سطر فارغ، مستند. غالبًا ما يكون المستند المرسل إلى خادم الويب فارغًا أو عبارة عن مجموعة من المعلمات parami=vali حيث vali هي قيمة أدخلها المستخدم في نموذج HTML.
تتخذ استجابة الخادم الشكل نفسه: رؤوس HTTP، سطر فارغ، مستند، حيث يمثل document هذه المرة المستند المطلوب من قبل متصفح العميل. إذا أرسل العميل معلمات، فإن المستند المرسَل يعتمد عمومًا على هذه المعلمات.
باستخدام متصفح Firefox، يمكن اكتشاف التبادلات الفعلية بين العميل وخادم الويب. هناك مكون إضافي لـ Firefox، يُسمى Firebug، يتيح تتبع هذه التبادلات. يتوفر Firebug على URL [https://addons.mozilla.org/fr/firefox/addon/firebug/]. إذا استخدمنا متصفح Firefox لزيارة هذا الرابط، فيمكننا عندئذٍ تنزيل المكون الإضافي Firebug. سنفترض في ما يلي أن المكون الإضافي Firebug قد تم تنزيله وتثبيته. وهو متاح عبر خيار في قائمة Firefox:
![]() |
تفتح نافذة Firebug داخل نافذة متصفح Firefox. وتحتوي هذه النافذة بدورها على قائمة:
![]() |
لاكتشاف التبادلات بين العميل والخادم أثناء طلب HTTP، نطلب URL [http://localhost/exemples-web/web_01.php] باستخدام متصفح Firefox. ثم تمتلئ نافذة Firebug بالمعلومات:
![]() |
فيما يلي ملخص للتبادل بين العميل والخادم:
- [1]: أرسل العميل الأمر HTTP: GET /exemples-web/web_01.php HTTP/1.1 لطلب المستند [web01.php]
- [2]: أرسل الخادم الرد: HTTP/1.1 200 OK مشيرًا إلى أنه عثر على المستند المطلوب.
يتيح Firebug الحصول على التبادلات الكاملة. يكفي "فتح" URL:
![]() |
نرى أعلاه رؤوس HTTP المتبادلة بين العميل (الطلب) والخادم (الاستجابة). من الممكن الحصول على الكود المصدري للتبادلات، c.a.d، وأسطر النص التي تم تبادلها فعليًا، [1]. وبذلك نحصل على الكود المصدري التالي:
![]() |
لكتابة برنامج نصي للعميل من خادم الويب، يكفي أن نحاكي سلوك المتصفح. بعد فتح اتصال مع الخادم، يمكن للبرنامج النصي للعميل إرسال الأسطر الثمانية للطلب أعلاه. في الواقع، ليست كلها ضرورية وسنرسل فقط الأسطر الثلاثة التالية:
- السطر 1: يحدد المستند المطلوب والبروتوكول المستخدم HTTP
- السطر 2: يعطي اسم جهاز البرنامج النصي للعميل
- السطر 3: يشير إلى أن العميل سيغلق الاتصال بالخادم بعد التبادل
لننتقل الآن إلى استجابة الخادم. نعلم أنها تم إنتاجها بواسطة البرنامج النصي PHP [web_01.php]. فيما سبق، نرى رؤوس الاستجابة HTTP. ويُظهر كود البرنامج النصي [web01.php] أنه لم يقم بإنشائها. لنتذكر تكوين البرنامج النصي للخادم:
![]() |
الخادم هو الذي أنشأ رؤوس الاستجابة HTTP. يمكن للبرنامج النصي للخادم إنشاؤها بنفسه. سنرى مثالاً على ذلك لاحقاً.
لقد ذكرنا أن استجابة خادم الويب كانت على الشكل التالي: رؤوس HTTP، سطر فارغ، مستند. إذا كان المستند مستندًا نصيًا، فيمكننا رؤيته في علامة التبويب [Réponse] في Firebug:
![]() |
تم إنشاء هذا الرد بواسطة البرنامج النصي [web_01.php].
10.1.2. عميل (client1_web_01)
نقوم الآن بكتابة برنامج نصي للعميل للخدمة السابقة. نعلم أن على العميل:
- فتح اتصال مع خادم الويب
- إرسال النص: رؤوس HTTP، سطر فارغ
- قراءة الرد الكامل من الخادم حتى يقوم هذا الأخير بإغلاق اتصاله مع العميل
- إغلاق الاتصال بالخادم
يتم تنفيذ البرنامج النصي للعميل في بيئة وحدة التحكم في NetBeans:
![]() |
- في [1]، يتم تضمين البرنامج النصي للعميل [client1_web_01.php] في مشروع Netbeans [exemples]
- في [2]، خصائص مشروع NetBeans [exemples]
- في [3]، يتم تشغيل مشروع NetBeans [exemples] في وضع «سطر الأوامر»، وهو ما أطلقنا عليه أيضًا اسم وضع «وحدة التحكم».
رمز البرنامج النصي للعميل هو التالي:
<?php
// البيانات
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_01.php";
// فتح اتصال على المنفذ 80 لـ $HOTE
$connexion = fsockopen($HOTE, $PORT);
// خطأ؟
if (!$connexion) {
print "Erreur : $erreur\n";
exit;
}
// يجب أن تنتهي رؤوس (headers) بروتوكول HTTP بسطر فارغ
// GET
fputs($connexion, "GET $urlServeur HTTP/1.1\n");
// المضيف
fputs($connexion, "Host: localhost\n");
// الاتصال
fputs($connexion,"Connection: close\n");
// سطر فارغ
fputs($connexion,"\n");
// سيقوم الخادم الآن بالرد على القناة $connexion. سيقوم بإرسال جميع
// بياناته ثم يغلق القناة. وبالتالي يقرأ العميل كل ما يصل من $connexion
// حتى إغلاق القناة
while ($ligne = fgets($connexion, 1000)) {
print "$ligne";
}//بينما
// يقوم العميل بدوره بإغلاق الاتصال
fclose($connexion);
// النهاية
exit;
تعليقات
- السطر 8: فتح اتصال بالخادم
- السطر 16: الأمر HTTP GET
- السطر 18: الأمر HTTP Host
- السطر 20: الأمر HTTP Connection
- السطر 22: سطر فارغ
- الأسطر 26-28: قراءة جميع أسطر النص المرسلة من الخادم حتى يقوم بإغلاق الاتصال.
- السطر 30: يقوم العميل بدوره بإغلاق الاتصال
النتائج
يؤدي تنفيذ البرنامج النصي للعميل إلى النتائج التالية:
تعليقات
- الأسطر 1-7: استجابة الخادم الويب HTTP.
- السطر 8: السطر الفارغ الذي يشير إلى نهاية الرؤوس HTTP
- الأسطر 9 وما بعدها: المستند. هنا، هو نص بسيط يمثل التاريخ والوقت الحاليين. هذا هو النص الذي كتبه البرنامج النصي PHP على المخرج رقم 1.
- السطر 1: يرد الخادم بأنه عثر على المستند المطلوب.
- السطر 2: التاريخ والوقت الحاليان للخادم
- السطر 3: هوية خادم الويب
- السطر 4: يشير إلى أن المستند التالي هو مستند تم إنشاؤه بواسطة البرنامج النصي PHP
- السطر 5: عدد أحرف المستند
- السطر 6: يشير الخادم إلى أنه بعد إرسال المستند، سيقوم بإغلاق الاتصال
- السطر 7: يشير إلى أن المستند المرسل من الخادم هو نص بتنسيق HTML. وهذا غير صحيح هنا. المستند عبارة عن نص بدون تنسيق معين. عندما لا يكون المستند نصًا بتنسيق HTML، يتعين على البرنامج النصي PHP الإشارة إلى ذلك. لم نقم بذلك هنا.
10.1.3. عميل ثانٍ (client2_web_01)
كان العميل السابق يعرض كل ما يرسله إليه خادم الويب. في الممارسة العملية، عادةً ما يتم تجاهل رؤوس الاستجابة HTTP واستخدام جزء المستند. نسعى هنا لاسترداد التاريخ والوقت المرسَلين من قبل البرنامج النصي PHP للخادم. سنستخرج هذه المعلومات باستخدام تعبير منتظم.
<?php
// استرداد المعلومات المرسلة من خادم الويب
// البيانات
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_01.php";
// فتح اتصال على المنفذ 80 لـ $HOTE
$connexion = fsockopen($HOTE, $PORT);
// خطأ؟
if (!$connexion) {
print "Erreur : $erreur\n";
exit;
}
// يجب أن تنتهي رؤوس (headers) بروتوكول HTTP بسطر فارغ
// GET
fputs($connexion, "GET $urlServeur HTTP/1.1\n");
// المضيف
fputs($connexion, "Host: localhost\n");
// الاتصال
fputs($connexion,"Connection: close\n");
// سطر فارغ
fputs($connexion,"\n");
// سيقوم الخادم الآن بالرد على القناة $connexion. وسيقوم بإرسال جميع
// هذه البيانات ثم يغلق القناة. وبالتالي، يقرأ العميل كل ما يصل من $connexion
// حتى يعثر على السطر الذي يبحث عنه بالصيغة jj/mm/aa hh:mm:ss
while ($ligne = fgets($connexion, 1000)) {
print "$ligne";
if (preg_match("/(\d\d)\/(\d\d)\/(\d\d) (\d\d):(\d\d):(\d\d)/", $ligne, $champs)) {
// نسترد الحقول #
array_shift($champs); // يتم حذف العنصر الأول من مصفوفة الحقول
// يتم استرداد الحقول الستة في 6 متغيرات
list($j, $m, $a, $h, $i, $s) = $champs;
// عرض النتيجة
print "\ndateheure=[$j,$m,$a,$h,$i,$s]\n";
}//if
}//while
// يقوم العميل بدوره بإنهاء الاتصال
fclose($connexion);
// النهاية
exit;
النتائج
10.2. استرداد الخادم للمعلمات المرسلة من العميل
في بروتوكول HTTP، يتوفر للعميل طريقتان لتمرير المعلمات إلى خادم الويب:
- يطلب URL من الخدمة بالشكل
GET url?param1=val1¶m2=val2¶m3=val3… HTTP/1.0
حيث يجب أن تخضع قيم vali مسبقًا لعملية ترميز بحيث يتم استبدال بعض الأحرف المحجوزة بقيمتها السداسية العشرية.
- يطلب URL من الخدمة بالصيغة
POST url HTTP/1.0
ثم يضع الرأس التالي ضمن الرؤوس HTTP المرسلة إلى الخادم:
تنتهي سلسلة الرؤوس المرسلة من قبل العميل بسطر فارغ. يمكنه بعد ذلك إرسال بياناته بالشكل
حيث يجب ترميز القيم vali مسبقًا، كما هو الحال مع الطريقة GET. يجب أن يكون عدد الأحرف المرسلة إلى الخادم هو N، حيث N هي القيمة المعلنة في الرأس
يسترد البرنامج النصي PHP المعلمات parami السابقة المرسلة من العميل، ويحصل على قيمها من المصفوفة:
- $_GET["parami"] لأمر GET
- $_POST["parami"] لأمر POST
10.2.1. العميل GET (client1_web_02)
يرسل البرنامج النصي PHP أدناه ثلاث معلمات [nom, prenom, age] إلى الخادم.
<?php
// العميل: يرسل الاسم الأول واسم العائلة والعمر إلى الخادم باستخدام الطريقة GET
// البيانات
$HOTE = "localhost";
$PORT = 80;
$URL = "/exemples-web/web_02.php";
list($prenom, $nom, $age) = array("jean-paul", "de la hûche", 45);
// الاتصال بخادم الويب
$connexion = fsockopen($HOTE, $PORT);
// العودة في حالة حدوث خطأ
if (!$connexion) {
print "Echec de la connexion au site ($HOTE,$PORT) : $erreur";
exit;
}//if
// إرسال المعلومات إلى الخادم PHP
// تشفير المعلومات
$infos = "prenom=" . urlencode(utf8_decode($prenom)) . "&nom=" . urlencode(utf8_decode($nom)) . "&age=" . urlencode("$age");
// متابعة وحدة التحكم
print "infos envoyées au serveur (GET)=$infos\n";
print "URL demandée=[$URL?$infos]\n\n";
// يجب أن تنتهي رؤوس (headers) بروتوكول HTTP بسطر فارغ
// GET
fputs($connexion, "GET $URL?$infos HTTP/1.1\n");
// المضيف
fputs($connexion, "Host: localhost\n");
// الاتصال
fputs($connexion,"Connection: close\n");
// سطر فارغ
fputs($connexion,"\n");
// سيقوم الخادم الآن بالرد على القناة $connexion. سيقوم بإرسال جميع
// بياناته ثم يغلق القناة. يقوم العميل بقراءة كل ما يصل من $connexion حتى إغلاق القناة
while ($ligne = fgets($connexion, 1000))
print "$ligne";
// يقوم العميل بدوره بإغلاق الاتصال
fclose($connexion);
تعليقات
- السطر 7: عنوان URL لبرنامج الخادم النصي
- السطر 8: قيم المعلمات الثلاثة
- السطر 10: فتح اتصال مع خادم الويب
- السطر 18: ترميز المعلمات الثلاثة. نحن في برنامج نصي مكتوب بلغة Netbeans مع ترميز الأحرف في UTF-8. لذا، يتم ترميز القيم الثلاث للمعلمات في السطر 8 في UTF-8. تقوم الدالة utf8_decode بتحويل ترميزها إلى ISO-8859-1. وبذلك، يمكن ترميزها إلى URL. يتم استبدال جميع الأحرف غير الأبجدية بـ %xx حيث xx هي القيمة السداسية العشرية للحرف. أما المسافات فيتم استبدالها بعلامة +.
- السطر 24: هل URL المطلوب هو $URL?$infos حيث يكون $infos على شكل nom=val1&prenom=val2&age=val3.
10.2.2. الخادم (web_02)
يكتفي الخادم بعرض ما يتلقاه.
<?php
// إدارة الأخطاء
ini_set("display_errors", "off");
// استرداد الخادم للمعلومات المرسلة من العميل
// هنا الاسم الأول=P&الاسم الأخير=N&العمر=A
// تتوفر هذه المعلومات تلقائيًا في المتغيرات
// $_GET['prenom']، $_GET['nom']، $_GET['age']
// يتم إرسالها إلى العميل
// رأس الرسالة UTF-8
header("Content-Type: text/plain; charset=utf-8");
// المعلمات المرسلة إلى الخادم
$prenom = isset($_GET['prenom']) ? $_GET['prenom'] : "";
$nom = isset($_GET['nom']) ? $_GET['nom'] : "";
$age = isset($_GET['age']) ? $_GET['age'] : "";
// الرد على العميل
$réponse = "informations reçues du client [" .
utf8_encode(htmlspecialchars($prenom, ENT_QUOTES)) .
"," . utf8_encode(htmlspecialchars($nom, ENT_QUOTES)) .
"," . utf8_encode(htmlspecialchars($age, ENT_QUOTES)) . "]\n";
print $réponse;
تعليقات
- السطر 13: يحدد رأس HTTP "Content-Type". بشكل افتراضي، يرسل خادم الويب الرأس
الذي يشير إلى أن الرد عبارة عن نص بتنسيق HTML. هنا، سيكون الرد عبارة عن نص بدون تنسيق مع أحرف مشفرة بتنسيق UTF-8:
يجب إرسال الرؤوس HTTP قبل استجابة الخادم. ولذلك، كما هو موضح أعلاه، سيأتي استدعاء الدالة header قبل أي تعليمة print.
- الأسطر 16-18: يتم استرداد المعلمات الثلاثة من المصفوفة $_GET.
- السطر 21: يتم إنشاء سلسلة الأحرف التي سيتم إرسالها كرد إلى العميل. بعض الأحرف لها معانٍ خاصة في HTML، ويجب استبدالها بكيانات HTML حتى يتم عرضها. تقوم الدالة htmlspecialchars($string) باستبدال جميع هذه الأحرف بمكافئاتها في السلسلة $string. على سبيل المثال، يتحول الحرف $ إلى &. وبما أننا ذكرنا في السطر 13 أن الرد سيكون النص UTF-8، فإننا نقوم بترميز القيم المستردة إلى UTF-8.
- السطر 25: يتم إرسال الرد إلى العميل
اختبار 1
لنقم بتشغيل البرنامج النصي [web_02] من NetBeans. عندئذٍ يتم تشغيل متصفح لعرض URL [http://localhost/exemples-web/web_02.php]:
![]() |
- في [1]، يعرض المتصفح URL [http://localhost/exemples-web/web_02.php]. نظرًا لأننا لم نضف أي معلمات إلى نهاية عنوان URL هذا، فقد رد الخادم بمعلمات فارغة. تذكر أن استجابة الخادم هي تلك المكتوبة باستخدام الأمر print.
- في [2]، نضيف معلمات إلى URL. هذه المرة، يقوم البرنامج النصي للخادم بإرجاعها بشكل صحيح.
تجدر الإشارة إلى أن NetBeans ليس ضروريًا لتنفيذ البرنامج النصي للخادم. يكفي كتابة URL الخاص بالبرنامج النصي للخادم في متصفح حتى يتم تنفيذ هذا البرنامج النصي.
الاختبار 2
نقوم بتنفيذ العميل [client1_web_02.php] في Netbeans. نتلقى الرد التالي:
- السطر 1: ترميز المعلمات الثلاثة. نلاحظ أن الحرف û قد تحول إلى %FB.
- السطر 12: استجابة الخادم
10.2.3. العميل POST (client2_web_03)
يرسل عميل HTTP إلى خادم الويب تسلسل النص التالي: رؤوس HTTP، سطر فارغ، المستند. في العميل السابق، كان هذا التسلسل كما يلي:
ولم يكن هناك مستند. وهناك طريقة أخرى لإرسال المعلمات، وهي الطريقة المعروفة باسم POST. وفي هذه الحالة، تكون سلسلة النص المرسلة إلى خادم الويب كما يلي:
هذه المرة، المعلمات التي كانت مضمنة في رؤوس HTTP بالنسبة للعميل GET، أصبحت، في العميل POST، جزءًا من المستند المرسل خلف الرؤوس.
نص البرنامج النصي للعميل POST هو كما يلي:
<?php
// العميل: يرسل الاسم الأول واسم العائلة والعمر إلى الخادم باستخدام الطريقة POST
// البيانات
$HOTE = "localhost";
$PORT = 80;
$URL = "/exemples-web/web_03.php";
list($prenom, $nom, $age) = array("jean-paul", "de la hûche", 45);
// الاتصال بخادم الويب
$connexion = fsockopen($HOTE, $PORT);
// العودة في حالة حدوث خطأ
if (!$connexion) {
print "Echec de la connexion au site ($HOTE,$PORT) : $erreur";
exit;
}//if
// إرسال المعلومات إلى الخادم PHP
// تشفير المعلومات
$infos = "prenom=" . urlencode(utf8_decode($prenom)) . "&nom=" . urlencode(utf8_decode($nom)) . "&age=" . urlencode("$age");
print "client : infos envoyées au serveur (POST) : $infos\n";
// يتم الاتصال بـ URL $URL عن طريق إرسال (POST) المعلمات إليه
// يجب أن تنتهي رؤوس (headers) بروتوكول HTTP بسطر فارغ
// POST
fputs($connexion, "POST $URL HTTP/1.1\n");
// المضيف
fputs($connexion, "Host: localhost\n");
// Connection
fputs($connexion,"Connection: close\n");
// نوع المحتوى
fputs($connexion, "Content-type: application/x-www-form-urlencoded\n");
// طول المحتوى
// يتم إرسال حجم (عدد الأحرف) المعلومات التي سيتم إرسالها
fputs($connexion, "Content-length: " . strlen($infos) . "\n");
// يتم إرسال سطر فارغ
fputs($connexion, "\n");
// يتم إرسال المعلومات
fputs($connexion, $infos);
// سيقوم الخادم الآن بالرد على القناة $connexion. سيقوم بإرسال جميع
// بياناته ثم يغلق القناة. يقرأ العميل كل ما يصل من $connexion
// حتى يتم إغلاق القناة
while ($ligne = fgets($connexion, 1000))
print "$ligne";
// ثم يقوم العميل بدوره بإغلاق الاتصال
fclose($connexion);
تعليقات
- السطر 7: URL لخدمة الويب التي سيتصل بها العميل POST. سيتم وصف خدمة الويب هذه قريبًا.
- السطر 8: المعلمات التي سيتم إرسالها إلى خدمة الويب
- السطر 10: الاتصال بخادم الويب
- السطر 18: ترميز المعلمات المراد إرسالها إلى خدمة الويب
- السطر 23: الأمر Http POST
- السطر 25: أمر Http Host
- السطر 27: الأمر Http Connection
- السطر 29: الأمر Http Content-type. لقد سبق أن صادفنا هذا الرأس HTTP. وهو موجود في كل مرة يتم فيها إرسال مستند. يستخدم خادم الويب الذي يرسل مستند HTML الرأس HTTP
وإذا أرسل نصًا غير منسق، فإنه يستخدم الرأس HTTP
عميلنا POST يرسل مستندًا عبارة عن نص بالصيغة param1=val1¶m2=val2&.... هذا النوع من المستندات له النوع application/x-www-form-urlencoded. لن نشرح السبب لأن ذلك سيجبرنا على شرح ماهية نموذج الويب.
- السطر 32: الأمر Content-length. لقد صادفنا هذا الرأس HTTP من قبل. وهو موجود في كل مرة يتم فيها إرسال مستند. ويشير إلى عدد بايتات المستند.
- السطر 34: السطر الفارغ الذي يشير إلى نهاية الرؤوس HTTP
- السطر 36: إرسال المعلمات
- السطران 40-41: قراءة الرد الكامل من الخادم
- السطر 43: إغلاق الاتصال
10.2.4. الخادم (web_03)
تقوم خدمة الويب [web_03] بنفس ما تقوم به خدمة الويب [web_02]. فهي تقرأ المعلمات المرسلة من العميل POST وتعيد إرسالها إلى العميل. وفيما يلي شفرة البرمجة الخاصة بها:
<?php
// إدارة الأخطاء
ini_set("display_errors", "off");
// رأس الرسالة UTF-8
header("Content-Type: text/plain; charset=utf-8");
// استرداد الخادم للمعلومات المرسلة من العميل
// هنا الاسم الأول=P&الاسم الأخير=N&العمر=A
// هذه المعلومات متاحة تلقائيًا في المتغيرات
// $_POST['prenom']، $_POST['nom']، $_POST['age']
// ويتم إرسالها إلى العميل
// المعلمات المرسلة إلى الخادم
$prenom = isset($_POST['prenom']) ? $_POST['prenom'] : "";
$nom = isset($_POST['nom']) ? $_POST['nom'] : "";
$age = isset($_POST['age']) ? $_POST['age'] : "";
// الرد على العميل
$réponse = "informations reçues du client [" .
utf8_encode(htmlspecialchars($prenom, ENT_QUOTES)) .
"," . utf8_encode(htmlspecialchars($nom, ENT_QUOTES)) .
"," . utf8_encode(htmlspecialchars($age, ENT_QUOTES)) . "]\n";
print $réponse;
تعليقات
- الأسطر 14-16: تصبح المعلمات المرسلة من قبل عميل POST متاحة في المصفوفة $_POST لخدمة الويب التي تستقبلها.
- السطر 6: رأس HTTP Content-Type. قد يثير الدهشة عدم وجود الرأس HTTP Content-Length في الرؤوس HTTP، وهو الرأس الذي يشير إلى حجم المستند الذي يتم إرساله إلى العميل. لقد رأينا أن خادم الويب يرسل رؤوس HTTP بشكل افتراضي. ورأس Content-Length هو جزء منها.
النتائج
بمجرد كتابة البرنامج النصي للخادم في NetBeans، يصبح متاحًا على الفور عبر خادم Apache تحت اسم WampServer. تذكر أن هذا يتم تحقيقه من خلال التكوين (انظر الفقرة 10). نقوم بتشغيل العميل الذي يستعلم من الخادم، ثم نتلقى الرد التالي:
- الأسطر 2-10: استجابة الخادم
- السطور 2-8: رؤوس Http
- السطر 10: المستند
- السطر 6: الرأس HTTP Content-Length. وبما أن هذا الرأس لم يتم إنشاؤه بواسطة البرنامج النصي للخادم، فإنه تم إنشاؤه بواسطة خادم الويب.
- السطر 8: الرأس الوحيد الذي أنشأه البرنامج النصي للخادم
10.3. استرداد متغيرات بيئة الخادم WEB
يتم تنفيذ برنامج نصي للخادم في بيئة ويب يمكنه التعرف عليها. يتم تخزين هذه البيئة في القاموس $_SERVER. نكتب أولاً تطبيق خادم يرسل إلى عملائه محتوى هذا القاموس.
10.3.1. الخادم (web_04)
<?php
// معالجة الأخطاء
ini_set("display_errors", "off");
// رأس الرسالة UTF-8
header("Content-Type: text/plain; charset=utf-8");
// إرسال قائمة المتغيرات المتاحة في بيئة الخادم إلى العميل
foreach ($_SERVER as $clé => $valeur) {
print "[$clé,$valeur]\n";
}
- يتم إرسال أزواج (المفتاح، القيمة) من القاموس $_SERVER إلى العملاء.
والنتيجة التي يتم الحصول عليها عندما يكون العميل متصفح ويب هي كما يلي:

فيما يلي معنى بعض المتغيرات (لنظام ويندوز. في نظام لينكس، ستكون مختلفة):
CMDE تمثل الرأس HTTP المرسلة من العميل. يمكن الوصول إلى جميع هذه الرؤوس. | |
مسار الملفات التنفيذية على الجهاز الذي يعمل عليه البرنامج النصي للخادم | |
مسار مترجم أوامر DOS | |
امتدادات الملفات القابلة للتنفيذ | |
مجلد تثبيت Windows | |
توقيع خادم الويب. لا يوجد شيء هنا. | |
نوع خادم الويب | |
اسم الإنترنت لجهاز خادم الويب | |
منفذ الاستماع لخادم الويب | |
عنوان جهاز خادم الويب IP | |
عنوان IP للعميل. في هذه الحالة، كان العميل على نفس الجهاز الذي يوجد عليه الخادم. | |
منفذ اتصال العميل | |
جذر شجرة المستندات التي يقدمها خادم الويب | |
البريد الإلكتروني لمسؤول خادم الويب | |
المسار الكامل لملف الخادم | |
إصدار بروتوكول HTTP المستخدم من قبل خادم الويب | |
أمر HTTP الذي يستخدمه العميل. هناك أربعة أوامر: GET، POST، PUT، DELETE | |
المعلمات المرسلة مع أمر GET /url?paramètres | |
URL المطلوب من قبل العميل. إذا طلب المتصفح URL http://machine[:port]/uri، فسيكون لدينا REQUEST_URI=uri | |
$_SERVER['SCRIPT_FILENAME']=$_SERVER['DOCUMENT_ROOT'].$_SERVER['SCRIPT_NAME'] |
10.3.2. العميل (client1_web_04)
يكتفي العميل بعرض كل ما يرسله إليه الخادم.
<?php
// البيانات
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_04.php";
// فتح اتصال على المنفذ 80 لـ $HOTE
$connexion = fsockopen($HOTE, $PORT);
// خطأ؟
if (!$connexion) {
print "Erreur : $erreur\n";
exit;
}
// يتم الاتصال بـ URL على خادم الويب
// يجب أن تنتهي رؤوس (headers) بروتوكول HTTP بسطر فارغ
// GET
fputs($connexion, "GET $urlServeur HTTP/1.1\n");
// المضيف
fputs($connexion, "Host: localhost\n");
// الاتصال
fputs($connexion,"Connection: close\n");
// سطر فارغ
fputs($connexion,"\n");
// سيقوم الخادم الآن بالرد على القناة $connexion. سيقوم بإرسال جميع
// بياناته ثم يغلق القناة. وبالتالي يقرأ العميل كل ما يصل من $connexion
// حتى يتم إغلاق القناة
while ($ligne = fgets($connexion, 1000)) {
print "$ligne";
}//بينما
// يقوم العميل بدوره بإغلاق الاتصال
fclose($connexion);
// النهاية
exit;
النتائج
10.4. إدارة الجلسات WEB
في أمثلة العميل/الخادم السابقة، كان العمل يسير على النحو التالي:
- يقوم العميل بفتح اتصال بالمنفذ 80 لجهاز خدمة الويب
- يرسل تسلسل النص: رؤوس Http، سطر فارغ، [document]
- رداً على ذلك، يرسل الخادم تسلسلاً من نفس النوع
- يغلق الخادم الاتصال مع العميل
- يقوم العميل بإغلاق الاتصال بالخادم
إذا أرسل نفس العميل طلبًا جديدًا إلى خادم الويب بعد ذلك بوقت قصير، يتم إنشاء اتصال جديد بين العميل والخادم. ولا يمكن للخادم معرفة ما إذا كان العميل الذي يتصل قد زاره من قبل أم أن هذا هو الطلب الأول. بين اتصالين، «ينسى» الخادم عميله. ولهذا السبب، يُقال إن بروتوكول HTTP هو بروتوكول عديم الحالة. ومع ذلك، من المفيد أن يتذكر الخادم عملائه. فعلى سبيل المثال، إذا كان التطبيق آمنًا، سيرسل العميل إلى الخادم اسم مستخدم وكلمة مرور للتعريف بنفسه. وإذا "نسي" الخادم عميله بين اتصالين، فسيضطر العميل إلى التعريف بنفسه عند كل اتصال جديد، وهو أمر غير مقبول.
ولمتابعة العميل، يتصرف الخادم على النحو التالي: عند تلقي الطلب الأول من العميل، يضمّن الخادم في رده معرّفًا يجب على العميل إرساله مرة أخرى في كل طلب جديد. وبفضل هذا المعرّف، الذي يختلف من عميل لآخر، يمكن للخادم التعرف على العميل. ويمكنه عندئذٍ إدارة ذاكرة خاصة بهذا العميل على شكل ملف مرتبط بشكل فريد بمعرّف العميل.
من الناحية التقنية، يحدث الأمر على النحو التالي:
- في الرد الموجه إلى عميل جديد، يضمّن الخادم الرأس HTTP Set-Cookie: MotClé=المعرّف. ولا يقوم بذلك إلا عند الطلب الأول.
- في الطلبات التالية، سيقوم العميل بإرسال معرّفه عبر رأس الرسالة HTTP، مع ملف تعريف الارتباط: MotClé=المعرّف، حتى يتعرف عليه الخادم.
قد يتساءل المرء كيف يعرف الخادم أنه يتعامل مع عميل جديد وليس مع عميل سبق له الزيارة. إن وجود الرأس HTTP Cookie في رؤوس HTTP الخاصة بالعميل هو ما يشير إلى ذلك. أما بالنسبة للعميل الجديد، فإن هذا الرأس يكون غائبًا.
يُطلق على مجموعة الاتصالات الخاصة بعميل معين اسم "جلسة".
10.4.1. ملف التكوين
لكي تعمل إدارة الجلسات بشكل صحيح مع PHP، يجب التحقق من أنه تم تكوينه بشكل صحيح. في نظام Windows، ملف التكوين الخاص به هو PHP.ini. وفقًا لسياق التشغيل (وحدة التحكم، الويب)، يجب البحث عن ملف التكوين [PHP.ini] في مجلدات مختلفة. لاكتشاف هذه المجلدات، سنستخدم البرنامج النصي التالي:
في السطر 4، توفر الدالة phpinfo معلومات عن المترجم PHP الذي يقوم بتنفيذ البرنامج النصي. وتقدم على وجه الخصوص مسار ملف التكوين [PHP.ini] المستخدم.
في بيئة وحدة التحكم، نحصل على نتيجة مشابهة لما يلي:
السطر 2: ملف التكوين الرئيسي هو c:\windows\PHP.ini
السطر 3: ملف التكوين الثانوي هو C:\serveursSGBD\wamp21\bin\PHP\php5.3.5\PHP.ini. وهو يتيح تعديل بعض خيارات التكوين في ملف التكوين الرئيسي.
في بيئة الويب، نحصل على النتيجة التالية:

ملف التكوين الثانوي هنا ليس هو نفسه الموجود في بيئة وحدة التحكم. وسنقوم بالرجوع إلى هذا الأخير. نجد في هذا الملف قسمًا خاصًا بالجلسة:
- السطر 1: يتم حفظ بيانات جلسة عمل العميل في ملف
- السطر 3: المجلد المخصص لحفظ بيانات الجلسة. إذا لم يكن هذا المجلد موجودًا، فلن يتم الإبلاغ عن أي خطأ ولن تعمل إدارة الجلسات.
- السطران 4-5: يشيران إلى أن معرف الجلسة يُدار بواسطة رؤوس Http Set-Cookie و Cookie
- السطر 6: سيكون رأس Set-Cookie بالصيغة Set-Cookie: PHPSESSID=identifiant_de_session
- السطر 7: لا يتم بدء جلسة عمل العميل تلقائيًا. يجب أن يطلبها البرنامج النصي للخادم صراحةً بواسطة تعليمة session_start().
10.4.2. الخادم 1 (web_05)
تتم إدارة معرّف الجلسة بشكل شفاف بالنسبة لخدمة الويب. ويتم إدارة هذا المعرّف بواسطة خادم الويب. يمكن لخدمة الويب الوصول إلى جلسة عمل العميل عبر الأمر session_start(). ومنذ تلك اللحظة، يمكن لخدمة الويب قراءة/كتابة البيانات في جلسة عمل العميل عبر القاموس $_SESSION. يوضح الكود التالي إدارة ثلاثة عدادات خلال الجلسة.
<?php
// إدارة الأخطاء
ini_set("display_errors", "off");
// رأس الصفحة UTF-8
header("Content-Type: text/plain; charset=utf-8");
// فتح جلسة
session_start();
// إدراج 3 متغيرات في الجلسة
if (!isset($_SESSION['N1'])) {
$_SESSION['N1'] = 0;
}
if (!isset($_SESSION['N2'])) {
$_SESSION['N2'] = 10;
}
if (!isset($_SESSION['N3'])) {
$_SESSION['N3'] = 100;
}
// زيادة قيم المتغيرات الثلاثة
$_SESSION['N1']++;
$_SESSION['N2']++;
$_SESSION['N3']++;
// إرسال المعلومات إلى العميل
print "N1=".$_SESSION['N1']."\n";
print "N2=".$_SESSION['N2']."\n";
print "N3=".$_SESSION['N3']."\n";
// إنهاء الجلسة
session_close();
- السطر 9: بدء جلسة عمل العميل
- الأسطر 11-13: المصفوفة $_SESSION هي قاموس أزواج (مفتاح، قيمة). تبقى البيانات الموضوعة في هذا القاموس ثابتة عبر طلبات العميل نفسه. إنها ذاكرة العميل على الخادم.
- الأسطر 11-19: إذا لم تكن العدادات الثلاثة N1 و N2 و N3 موجودة في الجلسة، يتم إدراجها فيها.
- الأسطر 21-23: يتم زيادة قيمها بمقدار وحدة واحدة
- الأسطر 25-27: يتم إرسال قيمها إلى العميل
في العلاقة بين العميل والخادم، تعتمد إدارة جلسة عمل العميل على الخادم على طرفين، هما العميل والخادم:
- يتولى الخادم مسؤولية إرسال معرّف إلى عميله عند الطلب الأول
- يتحمل العميل مسؤولية إعادة إرسال هذا المعرف مع كل طلب جديد. إذا لم يفعل ذلك، فسيعتقد الخادم أنه عميل جديد وسيقوم بإنشاء معرف جديد لجلسة جديدة.
النتائج
نستخدم متصفح الويب كعميل. بشكل افتراضي (في الواقع حسب التكوين)، يقوم المتصفح بإعادة إرسال معرفات الجلسة التي يرسلها الخادم إليه. مع توالي الطلبات، سيتلقى المتصفح العدادات الثلاثة المرسلة من الخادم وسيلاحظ زيادة قيمها.
![]() |
- إلى [1]، الطلب الأول إلى خدمة الويب [web_05]
- إلى [2]، والطلب الثالث يوضح أن العدادات قد زادت بالفعل. ومن الواضح أن قيم العدادات يتم حفظها مع توالي الطلبات.
لنستخدم Firebug لمشاهدة رؤوس HTTP المتبادلة بين الخادم والعميل. نغلق Firefox لإنهاء الجلسة الحالية مع الخادم، ثم نعيد فتحه ونقوم بتفعيل Firebug. نطلب الخدمة |web_05]:
![]() |
في الأعلى، نرى معرف الجلسة الذي أرسله الخادم في رده على الطلب الأول للعميل. ويستخدم الرأس HTTP Set-Cookie.
لنقم بإرسال طلب جديد عن طريق تحديث (F5) الصفحة في متصفح الويب:
![]() |
فيما سبق، نلاحظ أمرين:
- يعيد متصفح الويب معرف الجلسة مع الرأس HTTP Cookie.
- في ردها، لم تعد خدمة الويب تتضمن هذا المعرف. أصبح الآن على العميل مسؤولية إرساله مع كل طلب من طلباته.
10.4.3. العميل 1 (client1_web_05)
نقوم الآن بكتابة برنامج نصي للعميل مستندًا إلى البرنامج النصي للخادم السابق. في إدارة الجلسة، يجب أن يتصرف مثل متصفح الويب:
- في رد الخادم على طلبه الأول، يجب أن يعثر على معرّف الجلسة الذي يرسله إليه الخادم. وهو يعلم أنه سيجده في الرأس HTTP Set-Cookie.
- يجب عليه، في كل طلب لاحق، أن يعيد إرسال المعرف الذي تلقّاه إلى الخادم. وسيقوم بذلك باستخدام الرأس HTTP Cookie.
رمز العميل هو كما يلي:
<?php
// البيانات
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_05.php";
// اختبارات
$cookie = "";
for ($i = 0; $i < 5; $i++) {
list($erreur, $cookie, $N1, $N2, $N3) = connecte($HOTE, $PORT, $urlServeur, $cookie);
print "----------------------------\n";
print "client(erreur,cookie,N1,N2,N3)=[$erreur,$cookie,$N1,$N2,$N3]\n";
print "----------------------------\n";
}
// نهاية
exit;
function connecte($HOTE, $PORT, $urlServeur, $cookie) {
// توصيل العميل بـ ($HOTE،$PORT،$urlServeur)
// يرسل ملف تعريف الارتباط $cookie إذا كان غير فارغ
// يعرض جميع الأسطر المستلمة كرد
// فتح اتصال على المنفذ 80 لـ $HOTE
$connexion = fsockopen($HOTE, $PORT);
// خطأ؟
if (!$connexion)
return array("erreur lors de la connexion au serveur ($HOTE, $PORT)");
// يتم الاتصال بـ $urlserveur
// يجب أن تنتهي رؤوس (headers) بروتوكول HTTP بسطر فارغ
// GET
fputs($connexion, "GET $urlServeur HTTP/1.1\n");
// المضيف
fputs($connexion, "Host: localhost\n");
// تسجيل الدخول
fputs($connexion, "Connection: close\n");
// يتم إرسال ملف تعريف الارتباط إذا لم يكن فارغًا
if ($cookie) {
fputs($connexion, "Cookie: $cookie\n");
}//if
// إرسال سطر فارغ
fputs($connexion, "\n");
// يتم عرض استجابة خادم الويب
// ونحرص على استرداد ملف تعريف الارتباط (إن وجد) وقيم Ni
$N = "";
while ($ligne = fgets($connexion, 1000)) {
print "$ligne";
// ملف تعريف الارتباط - فقط عند الرد الأول
if (!$cookie) {
if (preg_match("/^Set-Cookie: (.*?)\s*$/", $ligne, $champs)) {
$cookie = $champs[1];
}
}
// قيمة N1
if (preg_match("/^N1=(.*?)\s*$/", $ligne, $champs))
$N1 = $champs[1];
// قيمة N2
if (preg_match("/^N2=(.*?)\s*$/", $ligne, $champs))
$N2 = $champs[1];
// قيمة ملف تعريف الارتباط N3
if (preg_match("/^N3=(.*?)\s*$/", $ligne, $champs))
$N3 = $champs[1];
}//بينما
// يتم إغلاق الاتصال
fclose($connexion);
// العودة
return array("", $cookie, $N1, $N2, $N3);
}
تعليقات
- الأسطر 3-16: البرنامج الرئيسي
- الأسطر 18-67: الدالة connecte
- الأسطر 9-14: يقوم العميل بالاتصال بالخادم 5 مرات ويعرض القيم المتتالية للعدادات N1 و N2 و N3. إذا تمت إدارة الجلسة بشكل صحيح، فيجب أن تزداد قيم هذه العدادات بمقدار 1 مع كل طلب جديد.
- السطر 10: تستخدم الدالة connecte المعلمات $HOTE و $PORT و $urlServeur لتوصيل العميل بخدمة الويب. يمثل المعلمة $cookie معرّف الجلسة. عند الاستدعاء الأول، تكون هذه السلسلة فارغة. أما في الاستدعاءات التالية، فهي تمثل معرّف الجلسة الذي أرسله الخادم ردًا على الاستدعاء الأول من العميل. تُرجع الدالة connecte في نتائجها قيم العدادات الثلاثة $N1 و $N2 و $N3، ومعرف الجلسة $cookie، وأي خطأ محتمل $erreur.
- السطر 18: تتميز الدالة connecte بخصائص عميل Http كلاسيكي. ونحن نعلق فقط على الميزات الجديدة.
- الأسطر 30-40: إرسال رؤوس Http.
- الأسطر 36-38: إذا كان معرف الجلسة معروفًا، يتم إرساله إلى الخادم
- الأسطر 44-66: معالجة جميع أسطر النص المرسلة من الخادم
- الأسطر 47-51: إذا لم يتم استرداد معرّف الجلسة بعد، يتم استرداده من الرأس HTTP Set-Cookie باستخدام تعبير منتظم.
- السطور 53-54: يتم الحصول على العداد N1 أيضًا باستخدام تعبير عادي
- السطور 56-57، 59-60: الأمر نفسه بالنسبة للعدادين N2 و N3
- السطر 63: إغلاق الاتصال بالخادم.
- السطر 65: إرجاع النتائج في شكل جدول.
النتائج
يؤدي تنفيذ البرنامج النصي للعميل إلى عرض ما يلي في وحدة التحكم Netbeans:
- السطر 5: في أول استجابة، يرسل الخادم معرف الجلسة. في الاستجابات التالية، لا يرسله بعد ذلك.
- من الواضح أن خادم الويب يحتفظ بقيم (N1، N2، N3) مع كل طلبات العميل. وهذا ما يُسمى بتتبع الجلسة.
يوضح المثالان التاليان أنه يمكن أيضًا حفظ قيم مصفوفة أو كائن.
10.4.4. الخادم 2 (web_06)
يوضح البرنامج النصي للخادم التالي أنه يمكن وضع مصفوفة أو قاموس في الجلسة.
<?php
// معالجة الأخطاء
ini_set("display_errors", "off");
// رأس الصفحة UTF-8
header("Content-Type: text/plain; charset=utf-8");
// فتح جلسة
session_start();
// تسجيل مصفوفة وقاموس
// تهيئة المصفوفة أو تعديلها
if (isset($_SESSION['tableau'])) {
for ($i = 0; $i < count($_SESSION['tableau']); $i++) {
$_SESSION['tableau'][$i]++;
}
} else {
for ($i = 0; $i < 10; $i++) {
$_SESSION['tableau'][$i] = $i * 10;
}
}
// تتم تهيئة القاموس أو تعديله
if (isset($_SESSION['dico'])) {
foreach (array_keys($_SESSION['dico']) as $clé) {
$_SESSION['dico'][$clé]++;
}
} else {
$_SESSION['dico'] = array("zéro" => 0, "dix" => 10, "vingt" => 20);
}
// إرسال المعلومات إلى العميل
print "tableau=" . join(",", $_SESSION['tableau']) . "\n";
print "dico=";
foreach ($_SESSION['dico'] as $clé => $valeur) {
print "($clé,$valeur) ";
}
print "\n";
تعليقات
- الأسطر 17-19: يتم تخزين المصفوفة في الجلسة في البداية إذا لم تكن موجودة فيها بالفعل
- الأسطر 12-15: إذا كان موجودًا بالفعل، يتم زيادة عناصره بمقدار 1
- السطر 27: يتم وضع قاموس ذي قيم رقمية في الجلسة إذا لم يكن موجودًا فيها بالفعل
- الأسطر 22-25: إذا كان موجودًا بالفعل، يتم زيادة قيمه الرقمية بمقدار 1
- الأسطر 30-35: يتم إرسال المصفوفة والقاموس إلى العميل
10.4.5. العميل 2 (client1_web_06)
<?php
// البيانات
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_06.php";
// اختبارات
$cookie = "";
for ($i = 0; $i < 5; $i++) {
connecte($HOTE, $PORT, $urlServeur, $cookie);
}
// النهاية
exit;
function connecte($HOTE, $PORT, $urlServeur, &$cookie) {
// توصيل العميل بـ ($HOTE، $PORT، $urlServeur)
// يرسل ملف تعريف الارتباط $cookie إذا كان غير فارغ
// يعرض جميع الأسطر المستلمة كرد
// يتم تمرير ملف تعريف الارتباط كمرجع ليتم مشاركته بين
// البرنامج المستدعى والبرنامج المستدعي
// فتح اتصال على المنفذ $PORT التابع لـ $HOTE
$connexion = fsockopen($HOTE, $PORT);
// خطأ؟
if (!$connexion)
return array("erreur lors de la connexion au serveur ($HOTE, $PORT)");
// يجب أن تنتهي رؤوس (headers) بروتوكول HTTP بسطر فارغ
// GET
fputs($connexion, "GET $urlServeur HTTP/1.1\n");
// المضيف
fputs($connexion, "Host: localhost\n");
// الاتصال
fputs($connexion, "Connection: close\n");
// يتم إرسال ملف تعريف الارتباط إذا لم يكن فارغًا
if ($cookie) {
fputs($connexion, "Cookie: $cookie\n");
}
// إرسال سطر فارغ
fputs($connexion, "\n");
// يتم عرض استجابة خادم الويب
// ونحرص على استرداد ملف تعريف الارتباط إن وجد
while ($ligne = fgets($connexion, 1000)) {
print "$ligne";
// ملف تعريف الارتباط - فقط عند الرد الأول
if (!$cookie) {
if (preg_match("/^Set-Cookie: (.*?)\s*$/", $ligne, $champs)) {
$cookie = $champs[1];
}
}
}
// يتم إغلاق الاتصال
fclose($connexion);
// العودة
return "";
}
كود العميل مشابه لكود العميل الذي تم شرحه سابقًا.
النتائج
10.4.6. الخادم 3 (web_07)
يُظهر البرنامج النصي للخادم التالي أنه يمكن وضع كائن في جلسة عمل.
<?php
// إدارة الأخطاء
ini_set("display_errors", "off");
// رأس الصفحة UTF-8
header("Content-Type: text/plain; charset=utf-8");
// فتح جلسة
session_start();
// تهيئة كائن «Personne» أو تعديله
if (isset($_SESSION['personne'])) {
$personne = $_SESSION['personne'];
// زيادة العمر
$personne->setAge($personne->getAge() + 1);
} else {
// يتم تعريف الشخص
$_SESSION['personne'] = new Personne("paul", "langévin", 10);
}
// العرض للعميل
print "personne=".$_SESSION['personne']."\n";
// النهاية
exit;
// ----------------------------------------------------------------
class Personne {
// سمات الفئة
private $prénom;
private $nom;
private $âge;
// أدوات الاسترجاع والتعيين
public function getPrénom() {
return $this->prénom;
}
public function getNom() {
return $this->nom;
}
public function getAge() {
return $this->âge;
}
public function setPrénom($prénom) {
$this->prénom = $prénom;
}
public function setNom($nom) {
$this->nom = $nom;
}
public function setAge($age) {
$this->âge = $age;
}
// المنشئ
function __construct($prénom, $nom, $âge) {
// نمر عبر وظائف التعيين
$this->setPrénom($prénom);
$this->setNom($nom);
$this->setAge($âge);
}
// طريقة toString
function __toString() {
return "[$this->prénom,$this->nom,$this->âge]";
}
}
تعليقات
- السطر 17: يتم تخزين كائن من النوع Personne في الجلسة إذا لم يكن موجودًا فيها بالفعل.
- الأسطر 11-15: إذا كان موجودًا بالفعل، يتم زيادة عمره بمقدار 1
- السطر 20: يتم إرسال الكائن من النوع Personne إلى العميل.
10.4.7. العميل 3 (client1_web_07)
<?php
// البيانات
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_07.php";
// اختبارات
$cookie = "";
for ($i = 0; $i < 5; $i++) {
connecte($HOTE, $PORT, $urlServeur, $cookie);
}//if
// نهاية
exit;
function connecte($HOTE, $PORT, $urlServeur, &$cookie) {
...
}
تعليقات
- السطر 15: الدالة connecte مطابقة لتلك الموجودة في البرنامج النصي للعميل السابق
النتائج




















