Skip to content

10. سرورهای PHP

از آنجایی که برنامه‌های PHP می‌توانند توسط یک سرور WEB اجرا شوند، چنین برنامه‌ای به یک برنامه سرور تبدیل می‌شود که قادر به خدمت‌رسانی به چندین مشتری است. از دیدگاه کلاینت، فراخوانی یک سرویس WEB معادل درخواست URL آن سرویس است. کلاینت می‌تواند به هر زبانی، از جمله PHP نوشته شود. در مورد دوم، ما از توابع شبکه‌ای که به تازگی بررسی کردیم استفاده می‌کنیم. ما همچنین باید بدانیم چگونه با یک سرویس WEB «ارتباط» برقرار کنیم، یعنی پروتکل ارتباطی http بین یک وب‌سرور و کلاینت‌های آن را درک کنیم. این هدف برنامه‌های بعدی است.

کلاینت وب توصیف‌شده در بخش 9.2 به ما امکان داده است تا بخشی از پروتکل HTTP را بررسی کنیم.

Image

در ساده‌ترین شکل، تبادله‌های کلاینت/سرور به شرح زیر است:

  • کلاینت یک اتصال با پورت ۸۰ روی وب‌سرور برقرار می‌کند
  • یک درخواست برای یک سند ارسال می‌کند
  • سرور وب سند درخواستی را ارسال می‌کند و اتصال را قطع می‌کند
  • سپس کلاینت اتصال را قطع می‌کند

کلاینت می‌تواند اشکال مختلفی داشته باشد: یک فایل متنی در قالب HTML، یک تصویر، یک ویدیو و غیره. این می‌تواند یک سند موجود (سند ایستا) یا سندی باشد که به صورت پویا توسط یک اسکریپت تولید می‌شود (سند پویا). در مورد دوم، به این امر برنامه‌نویسی وب گفته می‌شود. اسکریپت برای تولید پویا اسناد می‌تواند به زبان‌های مختلفی نوشته شود: PHP، پایتون، پرل، جاوا، روبی، سی‌شارپ، VB.net و غیره.

در اینجا، ما از PHP برای تولید پویا اسناد متنی استفاده می‌کنیم.

  • در [1]، کلاینت یک اتصال با سرور برقرار می‌کند، یک اسکریپت PHP را درخواست می‌کند و ممکن است پارامترها را به آن اسکریپت ارسال کند یا نکند
  • در [2]، سرور وب اسکریپت PHP را توسط تفسیرگر PHP اجرا می‌کند. این اسکریپت یک سند تولید می‌کند که به کلاینت [3] ارسال می‌شود.
  • سرور اتصال را قطع می‌کند. کلاینت نیز همین کار را انجام می‌دهد.

سرور وب می‌تواند همزمان چندین مشتری را مدیریت کند. با بسته نرم‌افزاری WampServer، سرور وب یک سرور آپاچی است، یک سرور متن‌باز از بنیاد آپاچی (http://www.apache.org/). در برنامه‌های کاربردی زیر، WampServer باید راه‌اندازی شود. این کار سه نرم‌افزار را فعال می‌کند: سرور وب آپاچی، SGBD و MySQL، و تفسیرگر PHP.

اسکریپت‌هایی که توسط وب‌سرور اجرا می‌شوند با استفاده از ابزار NetBeans نوشته خواهند شد. تا کنون، ما اسکریپت‌های PHP را نوشته‌ایم که در یک محیط کنسول اجرا می‌شوند:

کاربر از کنسول برای درخواست اجرای یک اسکریپت PHP و دریافت نتایج استفاده می‌کند.

در برنامه‌های کاربردی کلاینت/سرور که در ادامه می‌آیند،

  • اسکریپت سمت کلاینت در یک زمینه کنسول اجرا می‌شود
  • اسکریپت سرور در یک زمینه وب اجرا می‌شود

اسکریپت سرور PHP نمی‌تواند در هر نقطه‌ای از سیستم فایل قرار گیرد. این به این دلیل است که وب‌سرور برای اسناد ایستا و پویایی که درخواست ارائه آن‌ها را دارد، در مکان‌های مشخص‌شده توسط پیکربندی جستجو می‌کند. پیکربندی پیش‌فرض برای WampServer به این معناست که اسناد در پوشه <WampServer>/www جستجو می‌شوند، که در آن <WampServer> پوشه نصب WampServer است. بنابراین، اگر یک کلاینت وب با استفاده از مسیر URL [http://localhost/D]، سند 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/... خواهد بود). وب‌سرور محلی در WampServer، وب‌سرور آپاچی خواهد بود.
  • در [6]، ما URL پروژه را مشخص می‌کنیم. در اینجا، تصمیم می‌گیریم که اسکریپت S.php از پروژه به همراه URL و [http://localhost/exemples-web/S.php] درخواست شود. بر اساس موارد فوق، این بدان معناست که مسیر اسکریپت S.php در سیستم فایل، [<WampServer>/www/exemples-web/S.php] خواهد بود. این همان چیزی است که در [7] مشخص شده است. در اینجا درخواست شده است که هر اسکریپتی با نام S.php از پروژه در ساختار دایرکتوری وب‌سرور آپاچی کپی شود.
  • در [8]، پروژه جدید.

بیایید یک اسکریپت تست بنویسیم:

  • در [1]، اولین اسکریپت، PHP، را در پروژه [exemples-web] ایجاد می‌کنیم
  • در [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
//زمان: تعداد میلی‌ثانیه‌ها از ۱ ژانویه ۱۹۷۰
// قالب نمایش تاریخ و زمان
// d: روز (۲ رقم)
// m: ماه دو رقمی
// y: سال (۲ رقم)
// H: ساعت 0.23
// i: دقیقه
// s: ثانیه‌ها
print date("d/m/y H:i:s",time());

اساساً، اسکریپت PHP بالا زمان فعلی را روی صفحه نمایش می‌دهد. با این حال، هنگامی که توسط یک وب سرور اجرا می‌شود، جریان شماره ۱ – که معمولاً با صفحه نمایش مرتبط است – به اتصال رابط بین سرور و کلاینت آن هدایت می‌شود. بنابراین، در یک محیط وب، اسکریپت بالا زمان فعلی را به صورت متن برای کلاینت ارسال می‌کند.

بیایید این اسکریپت را در NetBeans اجرا کنیم:

  • در [1]، اسکریپت را اجرا می‌کنیم. سپس یک مرورگر وب باز می‌شود.
  • در [2]، URL که توسط مرورگر وب درخواست شده است
  • در [3]، متنی که توسط اسکریپت سرور ارسال شده است

مرورگر مشتری از پروتکل HTTP برای ارتباط با سرور وب استفاده می‌کند. ما قبلاً ساختار این پروتکل را شرح داده‌ایم.

کلاینت خطوط متنی را ارسال می‌کند که می‌توان آن‌ها را به سه بخش تقسیم کرد: سرآیندهای HTTP، یک خط خالی و سند. سندی که به سرور وب ارسال می‌شود معمولاً خالی است یا از مجموعه‌ای از پارامترهای parami=vali تشکیل شده است، که در آن vali مقداری است که کاربر در یک فرم HTML وارد می‌کند.

پاسخ سرور نیز ساختار مشابهی دارد: هدرها HTTP، یک خط خالی و یک سند، که در این مورد document سند درخواست‌شده توسط مرورگر کلاینت است. اگر کلاینت پارامترهایی ارسال کرده باشد، سند بازگشتی معمولاً به آن پارامترها بستگی دارد.

با استفاده از مرورگر فایرفاکس، می‌توان تبادلات واقعی بین کلاینت و سرور وب را مشاهده کرد. یک افزونه برای Firefox به نام Firebug وجود دارد که به شما امکان ردیابی این تبادلات را می‌دهد. Firebug در URL [https://addons.mozilla.org/fr/firefox/addon/firebug/] در دسترس است. اگر از مرورگر Firefox برای بازدید از این URL استفاده کنید، می‌توانید افزونه Firebug را دانلود کنید. از این پس فرض می‌کنیم که افزونه Firebug دانلود و نصب شده است. این افزونه از طریق گزینه‌ای در منوی Firefox در دسترس است:

 

یک پنجرهٔ Firebug در داخل پنجرهٔ مرورگر Firefox باز می‌شود. این پنجره منوی مخصوص به خود را دارد:

 

برای مشاهده مبادلات کلاینت/سرور در طول یک درخواست HTTP، ما درخواست URL [http://localhost/exemples-web/web_01.php] را با استفاده از مرورگر فایرفاکس انجام می‌دهیم. سپس پنجره فایربگ با اطلاعات پر می‌شود:

در بالا خلاصه‌ای از مبادلات کلاینت/سرور آورده شده است:

  • [1]: کلاینت فرمان HTTP را ارسال کرد: GET /web-examples/web_01.php HTTP/1.1 برای درخواست سند [web01.php]
  • [2]: سرور پاسخ را ارسال کرد: HTTP/1.1 200 OK، که نشان می‌دهد سند درخواستی را پیدا کرده است.

Firebug به شما امکان می‌دهد کل تبادل را مشاهده کنید. به سادگی URL را «گسترش» دهید:

در بالا، می‌توانیم سربرگ‌های HTTP مبادله شده بین کلاینت (درخواست) و سرور (پاسخ) را مشاهده کنیم. امکان بازیابی کد منبع این تبادل، c.a.d، و خطوط واقعی متن مبادله شده، [1]، وجود دارد. سپس کد منبع زیر را به دست می‌آوریم:

 

برای نوشتن یک اسکریپت سمت کلاینت برای سرور وب، کافی است رفتار مرورگر را شبیه‌سازی کنیم. پس از برقراری ارتباط با سرور، اسکریپت سمت کلاینت می‌تواند هشت خط درخواست را که در بالا نشان داده شده است ارسال کند. در واقع، همه آن‌ها ضروری نیستند و ما فقط سه خط زیر را ارسال خواهیم کرد:

1
2
3
GET /exemples-web/web01.php HTTP/1.1
Host: localhost
Connection: close
  • خط ۱: سند درخواستی و پروتکل HTTP استفاده‌شده را مشخص می‌کند
  • خط ۲: نام میزبان اسکریپت کلاینت را ارائه می‌دهد
  • خط ۳: نشان می‌دهد که پس از تبادل، کلاینت اتصال به سرور را قطع خواهد کرد

حال پاسخ سرور را بررسی می‌کنیم. می‌دانیم که این پاسخ توسط اسکریپت 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";
// اتصال بر روی پورت ۸۰ از $HOTE باز شد
$connexion = fsockopen($HOTE, $PORT);
// خطا؟
if (!$connexion) {
  print "Erreur : $erreur\n";
  exit;
}
// سربرگ‌های پروتکل 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;

توضیحات

  • خط ۸: باز کردن یک اتصال به سرور
  • خط 16: فرمان HTTP GET
  • خط ۱۸: فرمان HTTP میزبان
  • خط ۲۰: فرمان HTTP Connection
  • خط ۲۲: خط خالی
  • خطوط ۲۶–۲۸: تمام خطوط متنی را که توسط سرور ارسال می‌شود، تا زمانی که اتصال بسته شود، می‌خواند.
  • خط ۳۰: سپس کلاینت اتصال را قطع می‌کند

نتایج

اجرای اسکریپت سمت کلاینت نتایج زیر را تولید می‌کند:

1
2
3
4
5
6
7
8
9
HTTP/1.1 200 OK
Date: Wed, 17 Aug 2011 13:35:00 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Content-Length: 17
Connection: close
Content-Type: text/html

17/08/11 13:35:00

توضیحات

  • خطوط ۱–۷: پاسخ HTTP از سرور وب.
  • خط ۸: خط خالی که پایان سربرگ‌ها را نشان می‌دهد HTTP
  • از خط ۹ به بعد: سند. در این مورد، یک رشتهٔ متنی ساده است که نمایانگر تاریخ و زمان فعلی است. این متنی است که توسط اسکریپت PHP برای خروجی جریان ۱ نوشته شده است.
  • خط ۱: سرور پاسخ می‌دهد که سند درخواستی را یافته است.
  • خط ۲: تاریخ و زمان فعلی سرور
  • خط ۳: هویت وب‌سرور
  • خط ۴: نشان می‌دهد که سند بعدی توسط اسکریپت PHP تولید شده است
  • خط ۵: تعداد کاراکترهای سند
  • خط ۶: سرور نشان می‌دهد که پس از ارسال سند، اتصال را خواهد بست
  • خط ۷: نشان می‌دهد که سند ارسال‌شده توسط سرور، متنی در قالب HTML است. این در اینجا نادرست است. این سند یک متن ساده (plain text) است و هیچ قالب مشخصی ندارد. هنگامی که سند در قالب 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;
}
// سربرگ‌های پروتکل 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 را می‌خواند
//تا زمانی که خط مورد نظرش را در قالب dd/mm/yy 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)) {
     // fields # بازیابی می‌شوند
    array_shift($champs); // اولین عنصر را از آرایه 'fields' حذف می‌کند
     // شش فیلد را در شش متغیر بازیابی می‌کند
    list($j, $m, $a, $h, $i, $s) = $champs;
     //نمایش نتیجه
    print "\ndateheure=[$j,$m,$a,$h,$i,$s]\n";
  }//اگر
}//در حالی که
// کلاینت به نوبه خود اتصال را می‌بندد
fclose($connexion);
//پایان
exit;

نتایج

HTTP/1.1 200 OK
Date: Wed, 17 Aug 2011 14:14:51 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Content-Length: 17
Connection: close
Content-Type: text/html

17/08/11 14:14:51
dateheure=[17,08,11,14,14,51]

10.2. استخراج پارامترهای ارسال‌شده توسط کلاینت توسط سرور

در پروتکل HTTP، یک کلاینت دو روش برای ارسال پارامترها به سرور وب دارد:

  • این سرویس URL را به صورت درخواست می‌کند

GET url?param1=val1&param2=val2&param3=val3… HTTP/1.0

که در آن مقادیر vali باید ابتدا کدگذاری شوند تا برخی کاراکترهای رزرو شده با مقادیر هگزادسیمال خود جایگزین شوند.

  • این سرویس URL را به شکل درخواست می‌کند

POST url HTTP/1.0

سپس، در میان سربرگ‌های HTTP که به سرور ارسال می‌شوند، سربرگ زیر را شامل می‌شود:

Content-length=N

سرورهای باقی‌مانده‌ای که توسط کلاینت ارسال می‌شوند با یک خط خالی پایان می‌یابند. سپس می‌تواند داده‌های خود را به شکل زیر ارسال کند

val1&param2=val2&param3=val3…

که مقادیر vali باید، مانند روش GET، از قبل کدگذاری شوند. تعداد کاراکترهای ارسال‌شده به سرور باید N باشد، که N مقدار اعلام‌شده در هدر است

Content-length=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;
}//اگر
//ارسال اطلاعات به سرور 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";
// سربرگ‌های پروتکل 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);

توضیحات

  • خط ۷: URL اسکریپت سرور
  • خط ۸: مقادیر ۳ پارامتر
  • خط ۱۰: برقراری ارتباط با سرور وب
  • خط ۱۸: رمزگذاری سه پارامتر. این یک اسکریپت نوشته شده در NetBeans است که رمزگذاری کاراکتری آن روی UTF-8 تنظیم شده است. بنابراین، مقادیر سه پارامتر از خط ۸ در UTF-8 رمزگذاری می‌شوند. تابع utf8_decode آن‌ها را به ISO-8859-1 تبدیل می‌کند. پس از انجام این کار، می‌توان آن‌ها را برای URL رمزگذاری کرد. تمام کاراکترهای غیرالفبایی با %xx جایگزین می‌شوند، که در آن xx مقدار هگزادسیمال آن کاراکتر است. فاصله‌ها با علامت + جایگزین می‌شوند.
  • خط ۲۴: آیا URL درخواستی با $URL برابر است؟ $infos، در حالی که $infos به شکل nom=val1&prenom=val2&age=val3 است.

10.2.2. سرور (web_02)

سرور صرفاً هر آنچه دریافت می‌کند را نمایش می‌دهد.


<?php

// پردازش خطا
ini_set("display_errors", "off");

// سرور اطلاعات ارسال‌شده توسط کلاینت را بازیابی می‌کند
// در اینجا، first_name=P&last_name=N&age=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;

نظرات

  • خط ۱۳: هدر «Content-Type» را با مقدار HTTP تنظیم می‌کند. به طور پیش‌فرض، سرور وب این هدر را ارسال می‌کند
Content-Type: text/html

، که نشان می‌دهد پاسخ متنی در قالب HTML است. در اینجا، پاسخ متن بدون قالب‌بندی با کاراکترهای رمزگذاری‌شده در UTF-8 خواهد بود:

Content-Type: text/plain; charset=utf-8

سربرگ‌های HTTP باید قبل از پاسخ سرور ارسال شوند. بنابراین، همانطور که در بالا نشان داده شده است، فراخوانی تابع header باید قبل از هر دستور print انجام شود.

  • خطوط ۱۶–۱۸: سه پارامتر از آرایه $_GET بازیابی می‌شوند.
  • خط ۲۱: رشتهٔ کاراکتری که باید به کلاینت بازگردانده شود، ساخته می‌شود. برخی کاراکترها در HTML معانی ویژه‌ای دارند و برای نمایش باید با انتیت‌های HTML جایگزین شوند. تابع htmlspecialchars($string) همه این کاراکترها را با معادل‌هایشان در رشته $string جایگزین می‌کند. برای مثال، کاراکتر $ به & تبدیل می‌شود. سپس، از آنجا که در خط ۱۳ اعلام کردیم پاسخ متن UTF-8 خواهد بود، مقادیر بازیابی‌شده را به صورت UTF-8 رمزگذاری می‌کنیم.
  • خط ۲۵: پاسخ به کلاینت ارسال می‌شود

آزمون ۱

بیایید اسکریپت [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 اسکریپت سرور را در یک مرورگر تایپ کنید تا اسکریپت اجرا شود.

آزمون ۲

ما [client1_web_02.php] سمت کلاینت را در NetBeans اجرا می‌کنیم. پاسخ زیر را دریافت می‌کنیم:

infos envoyées au serveur (GET)=prenom=jean-paul&nom=de+la+h%FBche&age=45
URL demandée=[/exemples-web/web_02.php?prenom=jean-paul&nom=de+la+h%FBche&age=45]

HTTP/1.1 200 OK
Date: Wed, 17 Aug 2011 14:31:01 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Content-Length: 59
Connection: close
Content-Type: text/plain; charset=utf-8

informations reçues du client [jean-paul,de la hûche,45]
  • خط ۱: رمزگذاری سه پارامتر. می‌بینیم که کاراکتر 'û' به '%FB' تبدیل شده است.
  • خط ۱۲: پاسخ سرور

10.2.3. کلاینت POST (client2_web_03)

یک کلاینت HTTP توالی متنی زیر را به سرور وب ارسال می‌کند: سربرگ‌ها HTTP، خط خالی، سند. در کلاینت قبلی، این توالی به شرح زیر بود:

GET /url?paramètres HTTP/1.1
… autres entêtes HTTP
ligne vide

هیچ سندی وجود نداشت. روش دیگری برای ارسال پارامترها وجود دارد که به آن روش POST گفته می‌شود. در این حالت، توالی متنی که به سرور وب ارسال می‌شود به شرح زیر است:

POST /url HTTP/1.1
… autres entêtes HTTP
ligne vide
paramètres

این بار، پارامترهایی که برای کلاینت GET در هدرهای HTTP گنجانده شده بودند، بخشی از سند ارسال‌شده در پشت هدرها در کلاینت 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;
}//اگر
//ارسال اطلاعات به سرور 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) به آن
// سربرگ‌های پروتکل HTTP باید با یک خط خالی پایان یابند
//POST
fputs($connexion, "POST $URL HTTP/1.1\n");
// میزبان
fputs($connexion, "Host: localhost\n");
// اتصال
fputs($connexion,"Connection: close\n");
// نوع محتوا
fputs($connexion, "Content-type: application/x-www-form-urlencoded\n");
// Content-length
// اندازه (تعداد کاراکترها) داده‌های ارسالی مشخص می‌شود
fputs($connexion, "Content-length: " . strlen($infos) . "\n");
//یک خط خالی ارسال می‌شود
fputs($connexion, "\n");
// داده‌ها ارسال می‌شوند
fputs($connexion, $infos);
// سرور اکنون در کانال $connexion پاسخ خواهد داد. آن همه را ارسال خواهد کرد
//داده‌های آن را می‌خواند و سپس کانال را می‌بندد. کلاینت همه چیز را که از $connexion می‌رسد می‌خواند
//تا زمانی که کانال بسته شود
while ($ligne = fgets($connexion, 1000))
  print "$ligne";
//سپس کلاینت اتصال را قطع می‌کند
fclose($connexion);

نظرات

  • خط ۷: سرویس وب URL که کلاینت POST به آن متصل خواهد شد. این سرویس وب به زودی شرح داده خواهد شد.
  • خط ۸: پارامترهایی که باید به سرویس وب ارسال شوند
  • خط ۱۰: اتصال به سرور وب
  • خط ۱۸: رمزگذاری پارامترهای ارسالی به سرویس وب
  • خط ۲۳: فرمان HTTP POST
  • خط ۲۵: فرمان میزبان HTTP
  • خط ۲۷: هدر اتصال HTTP
  • خط ۲۹: هدر HTTP Content-Type. ما قبلاً با این هدر، HTTP، مواجه شده‌ایم. این هدر هر بار که یک سند ارسال می‌شود، وجود دارد. یک سرور وب که یک سند HTML ارسال می‌کند از هدر HTTP استفاده می‌کند.
Content-type : text/html

اگر متن بدون قالب‌بندی ارسال کند، از هدر HTTP استفاده می‌کند

Content-type : text/plain

کلاینت ما POST سندی را ارسال می‌کند که متنی در قالب param1=val1&param2=val2&... است. این نوع سند دارای نوع MIME application/x-www-form-urlencoded است. ما دلیل آن را توضیح نمی‌دهیم، زیرا این کار مستلزم توضیح این است که یک فرم وب چیست.

  • خط ۳۲: هدر Content-Length. ما قبلاً با این هدر، HTTP، مواجه شده‌ایم. این هدر در هر بار ارسال یک سند وجود دارد. این هدر تعداد بایت‌های موجود در سند را نشان می‌دهد.
  • خط ۳۴: خط خالی که پایان سرآیندها را نشان می‌دهد HTTP
  • خط ۳۶: ارسال پارامترها
  • خطوط ۴۰–۴۱: خواندن پاسخ کامل از سرور
  • خط ۴۳: بستن اتصال

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");

// سرور اطلاعات ارسال‌شده توسط کلاینت را بازیابی می‌کند
// در اینجا: first_name=P&last_name=N&age=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;

توضیحات

  • خطوط ۱۴–۱۶: پارامترهای ارسال‌شده توسط کلاینت POST در آرایه $_POST برای سرویس وب دریافت‌کننده آن‌ها در دسترس قرار می‌گیرند.
  • خط ۶: هدر HTTP Content-Type. شاید تعجب‌آور باشد که هدرهای HTTP شامل هدر HTTP Content-Length که نشان‌دهنده اندازه سند بازگردانده‌شده به کلاینت است، نمی‌شوند. ما دیده‌ایم که سرور وب به‌طور پیش‌فرض سربرگ‌های HTTP را ارسال می‌کند. سربرگ Content-Length یکی از آن‌هاست.

نتایج

پس از نوشتن اسکریپت سرور در NetBeans، آن بلافاصله از طریق سرور Apache در WampServer در دسترس قرار می‌گیرد. به یاد داشته باشید که این امر از طریق پیکربندی (به پاراگراف ۱۰ مراجعه کنید) محقق می‌شود. ما کلاینت را راه‌اندازی می‌کنیم، که از سرور پرس‌وجو می‌کند، و سپس پاسخ زیر را دریافت می‌کنیم:

client : infos envoyées au serveur (POST) : prenom=jean-paul&nom=de+la+h%FBche&age=45
HTTP/1.1 200 OK
Date: Sat, 20 Aug 2011 12:55:19 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Content-Length: 59
Connection: close
Content-Type: text/plain; charset=utf-8

informations reçues du client [jean-paul,de la hûche,45]
  • خطوط ۲–۱۰: پاسخ سرور
  • خطوط ۲–۸: سربرگ‌های HTTP
  • خط ۱۰: سند
  • خط ۶: هدر HTTP Content-Length. از آنجا که این هدر توسط اسکریپت سمت سرور تولید نشده است، باید توسط وب‌سرور تولید شده باشد.
  • خط ۸: تنها هدر تولیدشده توسط اسکریپت سرور

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 به کلاینت‌ها ارسال می‌شوند.

نتیجه‌ای که هنگام استفاده از مرورگر وب به دست می‌آید به شرح زیر است:

Image

در اینجا معنای برخی از متغیرها (برای ویندوز. در لینوکس، متفاوت خواهند بود) آورده شده است:

HTTP_CMDE
CMDE نمایانگر هدر HTTP ارسال‌شده توسط کلاینت است. تمام این هدرها قابل دسترسی هستند.
PATH
مسیر فایل‌های اجرایی روی ماشینی که اسکریپت سرور روی آن در حال اجرا است
COMSPEC
مسیر تفسیرگر دستورات DOS
PATHEXT
پسوندهای فایل‌های اجرایی
WINDIR
پوشه نصب ویندوز
SERVER_SIGNATURE
امضای وب‌سرور. در اینجا هیچ چیز نیست.
SERVER_SOFTWARE
نوع وب‌سرور
SERVER_NAME 
نام اینترنتی ماشین وب‌سرور
SERVER_PORT
پورت گوش‌دادن وب‌سرور
SERVER_ADDR
آدرس IP ماشین وب‌سرور
REMOTE_ADDR
آدرس کلاینت IP. در این مورد، کلاینت روی همان دستگاهی بود که سرور روی آن قرار داشت.
REMOTE_PORT
پورت ارتباطی کلاینت
DOCUMENT_ROOT
ریشهٔ درخت دایرکتوری برای اسناد ارائه‌شده توسط وب‌سرور
SERVER_ADMIN
آدرس ایمیل مدیر وب‌سرور
SCRIPT_FILENAME
مسیر کامل اسکریپت سرور
SERVER_PROTOCOL
نسخه پروتکل HTTP مورد استفاده توسط سرور وب
REQUEST_METHOD
روش HTTP مورد استفاده توسط کلاینت. چهار مورد وجود دارد: GET, POST, PUT, DELETE
QUERY_STRING
پارامترهای ارسال‌شده با فرمان GET /url?parameters
REQUEST_URI
URL درخواست‌شده توسط کلاینت. اگر مرورگر URL http://machine[:port]/uri را درخواست کند، ما REQUEST_URI=uri خواهیم داشت
SCRIPT_NAME
$_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
// سربرگ‌ها برای پروتکل 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;

نتایج

HTTP/1.1 200 OK
Date: Sat, 20 Aug 2011 14:11:58 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Content-Length: 1353
Connection: close
Content-Type: text/plain; charset=utf-8

[HTTP_HOST,localhost]
[HTTP_CONNECTION,close]
[PATH,C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\Microsoft SQL Server\90\Tools\binn\;...;]
[SystemRoot,C:\Windows]
[COMSPEC,C:\Windows\system32\cmd.exe]
[PATHEXT,.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC]
[WINDIR,C:\Windows]
[SERVER_SIGNATURE,]
[SERVER_SOFTWARE,Apache/2.2.17 (Win32) PHP/5.3.5]
[SERVER_NAME,localhost]
[SERVER_ADDR,127.0.0.1]
[SERVER_PORT,80]
[REMOTE_ADDR,127.0.0.1]
[DOCUMENT_ROOT,C:/serveursSGBD/wamp21/www/]
[SERVER_ADMIN,admin@localhost]
[SCRIPT_FILENAME,C:/serveursSGBD/wamp21/www/exemples-web/web_04.php]
[REMOTE_PORT,54552]
[GATEWAY_INTERFACE,CGI/1.1]
[SERVER_PROTOCOL,HTTP/1.1]
[REQUEST_METHOD,GET]
[QUERY_STRING,]
[REQUEST_URI,/exemples-web/web_04.php]
[SCRIPT_NAME,/exemples-web/web_04.php]
[PHP_SELF,/exemples-web/web_04.php]
[REQUEST_TIME,1313849518]

10.4. مدیریت جلسه WEB

در مثال‌های قبلی مشتری/سرور، فرایند به شرح زیر بود:

  • کلاینت یک اتصال به پورت ۸۰ روی ماشین سرویس وب باز می‌کند
  • آن توالی متن را ارسال می‌کند: سربرگ‌های HTTP، خط خالی، [document]
  • در پاسخ، سرور توالی‌ای از همان نوع را ارسال می‌کند
  • سرور ارتباط با کلاینت را قطع می‌کند
  • کلاینت اتصال به سرور را قطع می‌کند

اگر همان کلاینت کمی بعد درخواست جدیدی به وب سرور ارسال کند، یک اتصال جدید بین کلاینت و سرور برقرار می‌شود. سرور نمی‌تواند تشخیص دهد که آیا کلاینت متصل‌شونده قبلاً بازدید کرده است یا اینکه این اولین درخواست اوست. بین اتصالات، سرور کلاینت خود را «فراموش می‌کند». به همین دلیل، پروتکل HTTP یک پروتکل بدون حالت (stateless) نامیده می‌شود. با این حال، به یاد داشتن مشتریان برای سرور مفید است. به عنوان مثال، اگر یک برنامه امن باشد، مشتری برای احراز هویت خود، نام کاربری و رمز عبور را برای سرور ارسال می‌کند. اگر سرور بین اتصالات، مشتری خود را «فراموش» کند، مشتری مجبور است در هر اتصال جدید، خود را احراز هویت کند، که این کار شدنی نیست.

برای پیگیری یک کلاینت، سرور به شرح زیر عمل می‌کند: هنگامی که کلاینت اولین درخواست خود را ارسال می‌کند، سرور یک شناسه را در پاسخ خود قرار می‌دهد که کلاینت باید آن را با هر درخواست بعدی بازگرداند. به لطف این شناسه که برای هر کلاینت منحصربه‌فرد است، سرور می‌تواند کلاینت را تشخیص دهد. سپس می‌تواند یک رکورد برای آن کلاینت به شکل فایلی که به طور منحصربه‌فرد با شناسه کلاینت مرتبط است، نگهداری کند.

از نظر فنی، این به شرح زیر عمل می‌کند:

  • در پاسخ خود به یک مشتری جدید، سرور هدر HTTP Set-Cookie: MotClé=Identifier را درج می‌کند. این کار را فقط در اولین درخواست انجام می‌دهد.
  • در درخواست‌های بعدی، کلاینت شناسهٔ خود را از طریق هدر HTTP ارسال می‌کند: MotClé=Identifier، تا سرور بتواند آن را شناسایی کند.

ممکن است این سؤال پیش بیاید که سرور چگونه می‌فهمد در حال سروکار داشتن با یک مشتری جدید است نه یک مشتری بازگشتی. حضور هدرهای HTTP و Cookie در هدرهای HTTP مشتری به سرور این موضوع را اطلاع می‌دهد. برای یک مشتری جدید، این هدرها وجود ندارند.

تمام اتصالات انجام‌شده توسط یک کلاینت مشخص، یک جلسه (session) نامیده می‌شود.

10.4.1. فایل پیکربندی

برای اطمینان از اینکه مدیریت جلسه با PHP به درستی کار می‌کند، باید بررسی کنید که پیکربندی آن به درستی انجام شده است. در ویندوز، فایل پیکربندی آن PHP.ini است. بسته به زمینهٔ اجرا (کنسول، وب)، فایل پیکربندی [PHP.ini] باید در پوشه‌های متفاوتی قرار گیرد. برای شناسایی این پوشه‌ها، از اسکریپت زیر استفاده کنید:

1
2
3
4
<?php

// اطلاعات PHP
phpinfo();

در خط ۴، تابع phpinfo اطلاعاتی در مورد تفسیرگر PHP که اسکریپت را اجرا می‌کند، ارائه می‌دهد. به طور خاص، این تابع مسیر فایل پیکربندی [PHP.ini] در حال استفاده را ارائه می‌کند.

در یک محیط کنسول، خروجی مشابه موارد زیر است:

1
2
3
4
5
6
Configuration File (PHP.ini) Path => C:\Windows
Loaded Configuration File => C:\serveursSGBD\wamp21\bin\PHP\php5.3.5\PHP.ini
Scan this dir for additional .ini files => (none)
Additional .ini files parsed => (none)

خط ۲: فایل پیکربندی اصلی در c:\windows\PHP.ini قرار دارد

خط ۳: یک فایل پیکربندی ثانویه در C:\serveursSGBD\wamp21\bin\PHP\php5.3.5\PHP.ini قرار دارد. این امکان را به شما می‌دهد که برخی گزینه‌های پیکربندی را در فایل پیکربندی اصلی تغییر دهید.

در یک محیط وب، نتیجه زیر به دست می‌آید:

Image

فایل پیکربندی ثانویه در اینجا با آنچه در محیط کنسول است متفاوت است. ما دومی را بررسی خواهیم کرد. در این فایل، بخشی به نام «session» وجود دارد:

[Session]
session.save_handler = files
session.save_path = "C:/serveursSGBD/wamp21/tmp"
session.use_cookies = 1
session.use_only_cookies = 1
session.name = PHPSESSID
session.auto_start = 0
session.cookie_lifetime = 0
session.cookie_path = /
session.cookie_domain =
session.serialize_handler = PHP
session.cache_expire = 180
session.hash_function = 0
  • خط ۱: داده‌های جلسهٔ کلاینت در یک فایل ذخیره می‌شود
  • خط ۳: پوشه‌ای که داده‌های جلسه در آن ذخیره می‌شود. اگر این پوشه وجود نداشته باشد، هیچ خطایی گزارش نمی‌شود و مدیریت جلسه کار نمی‌کند.
  • خطوط ۴–۵: نشان می‌دهند که شناسه جلسه توسط سربرگ‌های HTTP Set-Cookie و Cookie مدیریت می‌شود
  • خط ۶: هدر Set-Cookie به شکل Set-Cookie: PHPSESSID=identifiant_de_session خواهد بود.
  • خط ۷: یک جلسهٔ مشتری به‌طور خودکار آغاز نمی‌شود. اسکریپت سرور باید صراحتاً با استفاده از دستور session_start() آن را درخواست کند.

10.4.2. سرور ۱ (web_05)

مدیریت شناسه جلسه برای سرویس وب شفاف است. این شناسه توسط وب‌سرور مدیریت می‌شود. یک سرویس وب از طریق دستور session_start() به جلسهٔ مشتری دسترسی دارد. از این نقطه به بعد، سرویس وب می‌تواند از طریق دیکشنری $_SESSION از جلسهٔ مشتری بخواند و در آن بنویسد. کد زیر مدیریت مبتنی بر جلسهٔ سه شمارنده را نشان می‌دهد.


<?php

// پردازش خطا
ini_set("display_errors", "off");
// سربرگ UTF-8
header("Content-Type: text/plain; charset=utf-8");

// ورود
session_start();
// تنظیم ۳ متغیر در جلسه
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();
  • خط ۹: شروع یک جلسه مشتری
  • خطوط ۱۱–۱۳: آرایه $_SESSION یک دیکشنری کلید-مقدار است. داده‌های ذخیره‌شده در این دیکشنری در درخواست‌های مختلف از سوی همان کلاینت باقی می‌ماند. این آرایه مانند حافظه کلاینت روی سرور عمل می‌کند.
  • خطوط ۱۱–۱۹: اگر سه شمارنده N1، N2 و N3 در جلسه نباشند، به آن اضافه می‌شوند.
  • خطوط 21–23: مقدار آن‌ها یک واحد افزایش می‌یابد
  • خطوط ۲۵–۲۷: مقادیر آن‌ها به کلاینت ارسال می‌شوند

در رابطهٔ کلاینت–سرور، مدیریت جلسهٔ کلاینت بر روی سرور به هر دو طرف، کلاینت و سرور، بستگی دارد:

  • سرور مسئول ارسال یک شناسه‌ به کلاینت در اولین درخواست آن است
  • کلاینت مسئول بازگرداندن این شناسه‌ با هر درخواست جدید است. اگر این کار را انجام ندهد، سرور فرض می‌کند که کلاینت جدید است و برای یک جلسه جدید شناسه‌ای جدید تولید می‌کند.

نتایج

ما از یک مرورگر وب به‌عنوان کلاینت استفاده می‌کنیم. به‌طور پیش‌فرض (یا بهتر است بگوییم، بر اساس پیکربندی)، مرورگر در واقع شناسه‌های جلسه‌ای را که سرور برایش ارسال می‌کند، به سرور بازمی‌گرداند. با ارسال درخواست‌ها، مرورگر سه شمارنده ارسالی از سوی سرور را دریافت می‌کند و شاهد افزایش مقادیر آن‌ها خواهد بود.

  • به [1]، اولین درخواست به سرویس وب [web_05]
  • به [2]؛ سومین درخواست نشان می‌دهد که شمارنده‌ها واقعاً افزایش یافته‌اند. مقادیر شمارنده‌ها واقعاً در طول درخواست‌ها حفظ می‌شوند.

بیایید از Firebug برای مشاهده سربرگ‌های HTTP مبادله شده بین سرور و کلاینت استفاده کنیم. فایرفاکس را می‌بندیم تا جلسه جاری با سرور را پایان دهیم، آن را دوباره باز می‌کنیم و Firebug را فعال می‌کنیم. درخواستی به سرویس [web_05] ارسال می‌کنیم:

در بالا، می‌توانیم شناسه جلسه (session ID) را که سرور در پاسخ به اولین درخواست کلاینت ارسال کرده است، مشاهده کنیم. این شناسه از هدر HTTP Set-Cookie استفاده می‌کند.

بیایید با تازه‌سازی (F5) صفحه در مرورگر وب، یک درخواست جدید ارسال کنیم:

دو نکته در اینجا قابل توجه است:

  • مرورگر وب شناسه جلسه را با هدرهای HTTP و Cookie بازمی‌گرداند.
  • در پاسخ خود، سرویس وب دیگر این شناسه را شامل نمی‌شود. اکنون این مسئولیت بر عهده کلاینت است که آن را با هر درخواست خود ارسال کند.

10.4.3. کلاینت ۱ (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 را ارسال می‌کند اگر خالی نباشد
  //تمام خطوط دریافت‌شده در پاسخ را نمایش می‌دهد
  //یک اتصال روی پورت ۸۰ $HOTE باز می‌کند
  $connexion = fsockopen($HOTE, $PORT);
  //خطا؟
  if (!$connexion)
    return array("erreur lors de la connexion au serveur ($HOTE, $PORT)");
  // به $urlserveur متصل می‌شود
  // سربرگ‌های پروتکل 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");
  // نمایش پاسخ سرور وب
  //و اطمینان حاصل می‌کند که هر کوکی و مقادیر 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);
}

توضیحات

  • خطوط ۳–۱۶: برنامه اصلی
  • خطوط ۱۸–۶۷: تابع connecte
  • خطوط ۹–۱۴: کلاینت پنج بار به سرور فراخوانی می‌کند و مقادیر متوالی شمارنده‌های N1، N2 و N3 را نمایش می‌دهد. اگر جلسه به‌درستی مدیریت شود، این شمارنده‌ها باید با هر درخواست جدید، ۱ واحد افزایش یابند.
  • خط ۱۰: تابع connecte از پارامترهای $HOTE، $PORT و $urlServeur برای اتصال کلاینت به سرویس وب استفاده می‌کند. پارامتر $cookie نمایانگر شناسهٔ جلسه است. در اولین فراخوانی، این یک رشتهٔ خالی است. در فراخوانی‌های بعدی، این شناسهٔ جلسه‌ای است که سرور در پاسخ به اولین فراخوانی مشتری ارسال می‌کند. تابع connecte، به عنوان نتایج، مقادیر سه شمارنده $N1، $N2، را برمی‌گرداند، $N3، شناسه جلسه $cookie و هر خطایی $erreur.
  • خط ۱۸: تابع connecte دارای ویژگی‌های یک کلاینت استاندارد HTTP است. ما تنها دربارهٔ ویژگی‌های جدید توضیح می‌دهیم.
  • خطوط ۳۰–۴۰: ارسال هدرهای HTTP.
  • خطوط ۳۶–۳۸: اگر شناسهٔ جلسه مشخص باشد، به سرور ارسال می‌شود
  • خطوط ۴۴–۶۶: پردازش تمام خطوط متنی ارسال‌شده توسط سرور
  • خطوط ۴۷–۵۱: اگر شناسه جلسه هنوز بازیابی نشده باشد، با استفاده از یک عبارت منظم از هدر HTTP Set-Cookie استخراج می‌شود.
  • خطوط ۵۳–۵۴: شمارنده N1 نیز با استفاده از یک عبارت منظم بازیابی می‌شود
  • خطوط ۵۶–۵۷، ۵۹–۶۰: همین امر در مورد شمارنده‌های N2 و N3 نیز صدق می‌کند
  • خط ۶۳: اتصال به سرور بسته می‌شود.
  • خط ۶۵: نتایج به صورت یک آرایه بازگردانده می‌شوند.

نتایج

اجرای اسکریپت کلاینت باعث نمایش موارد زیر در کنسول NetBeans می‌شود:

HTTP/1.1 200 OK
Date: Sun, 21 Aug 2011 13:59:17 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Set-Cookie: PHPSESSID=ohiqtkv7hu2b26kdshjtqms9p7; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 18
Connection: close
Content-Type: text/plain; charset=utf-8

N1=1
N2=11
N3=101
----------------------------
client(erreur,cookie,N1,N2,N3)=[,PHPSESSID=ohiqtkv7hu2b26kdshjtqms9p7; path=/,1,11,101]
----------------------------
HTTP/1.1 200 OK
Date: Sun, 21 Aug 2011 13:59:18 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 18
Connection: close
Content-Type: text/plain; charset=utf-8

N1=2
N2=12
N3=102
----------------------------
client(erreur,cookie,N1,N2,N3)=[,PHPSESSID=ohiqtkv7hu2b26kdshjtqms9p7; path=/,2,12,102]
----------------------------
HTTP/1.1 200 OK
Date: Sun, 21 Aug 2011 13:59:18 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 18
Connection: close
Content-Type: text/plain; charset=utf-8

N1=3
N2=13
N3=103
----------------------------
client(erreur,cookie,N1,N2,N3)=[,PHPSESSID=ohiqtkv7hu2b26kdshjtqms9p7; path=/,3,13,103]
----------------------------
HTTP/1.1 200 OK
Date: Sun, 21 Aug 2011 13:59:18 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 18
Connection: close
Content-Type: text/plain; charset=utf-8

N1=4
N2=14
N3=104
----------------------------
client(erreur,cookie,N1,N2,N3)=[,PHPSESSID=ohiqtkv7hu2b26kdshjtqms9p7; path=/,4,14,104]
----------------------------
HTTP/1.1 200 OK
Date: Sun, 21 Aug 2011 13:59:18 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 18
Connection: close
Content-Type: text/plain; charset=utf-8

N1=5
N2=15
N3=105
----------------------------
client(erreur,cookie,N1,N2,N3)=[,PHPSESSID=ohiqtkv7hu2b26kdshjtqms9p7; path=/,5,15,105]
  • خط ۵: در اولین پاسخ، سرور شناسه جلسه را ارسال می‌کند. در پاسخ‌های بعدی دیگر آن را ارسال نمی‌کند.
  • واضح است که وب‌سرور مقادیر (N1, N2, N3) را در درخواست‌های متوالی کلاینت حفظ می‌کند. این امر به عنوان ردیابی جلسه (session tracking) شناخته می‌شود.

دو مثال زیر نشان می‌دهد که ذخیره کردن مقادیر یک آرایه یا یک شیء نیز امکان‌پذیر است.

10.4.4. سرور ۲ (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: اگر آرایه قبلاً در جلسه وجود نداشته باشد، در ابتدا در جلسه ذخیره می‌شود
  • خطوط ۱۲–۱۵: اگر قبلاً در جلسه وجود داشته باشد، عناصر آن ۱ واحد افزایش می‌یابند
  • خط ۲۷: اگر دیکشنری با مقادیر عددی قبلاً در جلسه وجود نداشته باشد، به جلسه اضافه می‌شود
  • خطوط ۲۲–۲۵: اگر قبلاً در جلسه وجود داشته باشد، مقادیر عددی آن ۱ واحد افزایش می‌یابند
  • خطوط ۳۰–۳۵: آرایه و دیکشنری به کلاینت ارسال می‌شوند

10.4.5. کلاینت ۲ (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)");
  // سربرگ‌های پروتکل 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 "";
}

کد کلاینت مشابه کد کلاینتی است که قبلاً در مورد آن توضیح داده شده است.

نتایج

HTTP/1.1 200 OK
...
Set-Cookie: PHPSESSID=6lvttr0uhpj5q3sl91h4h7p322; path=/
...

tableau=0,10,20,30,40,50,60,70,80,90
dico=(zéro,0) (dix,10) (vingt,20) 

HTTP/1.1 200 OK
...

tableau=1,11,21,31,41,51,61,71,81,91
dico=(zéro,1) (dix,11) (vingt,21) 

HTTP/1.1 200 OK
...

tableau=2,12,22,32,42,52,62,72,82,92
dico=(zéro,2) (dix,12) (vingt,22) 

HTTP/1.1 200 OK
...

tableau=3,13,23,33,43,53,63,73,83,93
dico=(zéro,3) (dix,13) (vingt,23) 

HTTP/1.1 200 OK
...

tableau=4,14,24,34,44,54,64,74,84,94
dico=(zéro,4) (dix,14) (vingt,24) 

10.4.6. سرور ۳ (web_07)

اسکریپت سرور زیر نشان می‌دهد که یک شیء می‌تواند در یک جلسه قرار داده شود.


<?php

// مدیریت خطا
ini_set("display_errors", "off");
// سربرگ UTF-8
header("Content-Type: text/plain; charset=utf-8");

// ورود
session_start();
// ابتدایی‌سازی یا تغییر یک شیء Person
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]";
  }

}

توضیحات

  • خط ۱۷: اگر شیئی از نوع Personne قبلاً در جلسه وجود نداشته باشد، در جلسه قرار می‌گیرد.
  • خطوط ۱۱–۱۵: اگر قبلاً در جلسه باشد، سن آن ۱ واحد افزایش می‌یابد
  • خط ۲۰: شیء از نوع Personne به کلاینت ارسال می‌شود.

10.4.7. کلاینت ۳ (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);
}//اگر
//پایان
exit;

function connecte($HOTE, $PORT, $urlServeur, &$cookie) {
...
}

توضیحات

  • خط ۱۵: تابع connecte با تابع موجود در اسکریپت کلاینت قبلی یکسان است

نتایج

HTTP/1.1 200 OK
...

personne=[paul,langévin,10]

HTTP/1.1 200 OK
...

personne=[paul,langévin,11]

HTTP/1.1 200 OK
...

personne=[paul,langévin,12]

HTTP/1.1 200 OK
...

personne=[paul,langévin,13]

HTTP/1.1 200 OK
...

personne=[paul,langévin,14]