11. تمرين IMPOTS باستخدام خدمة WEB وبنية ثلاثية الطبقات
سنستأنف التمرين IMPOTS (انظر الفقرات 4.2 و4.3 و6) وسنقوم بتحويله إلى تطبيق عميل/خادم. سيتم تقسيم البرنامج النصي للخادم إلى ثلاثة عناصر:
- طبقة تسمى [dao] (كائنات الوصول إلى البيانات) ستتولى التبادل مع قاعدة البيانات MySQL
- طبقة تسمى [métier] ستقوم بحساب الضريبة
- طبقة [web] التي ستتولى التبادل مع عملاء الويب.
![]() |
البرنامج النصي للعميل [1]:
- يرسل إلى البرنامج النصي للخادم المعلومات الثلاث ($marié، $enfants، $salaire) اللازمة لحساب الضريبة
- يعرض استجابة الخادم على وحدة التحكم
يتكون البرنامج النصي للخادم [2] من الطبقة [web] الخاصة بالخادم.
- عند بدء جلسة عمل جديدة للعميل، سيقوم بوضع بيانات قاعدة البيانات MySQL و[dbimpots] في مصفوفات. وللقيام بذلك، سيستعين بالطبقة [dao]. سيتم وضع الجداول التي تم إنشاؤها بهذه الطريقة في جلسة عمل العميل حتى يمكن استخدامها في طلبات العميل اللاحقة.
- عند إجراء استعلام من العميل، سيقوم بتمرير المعلومات الثلاثة التالية ($marié، $enfants، $salaire) إلى الطبقة [métier] التي ستحسب الضريبة $impot.
- سيعيد البرنامج النصي للخادم الضريبة المحسوبة $impôt.
11.1. برنامج العميل النصي (clients_impots_05_web)
سيكون البرنامج النصي للعميل عميلاً لخدمة الويب لحساب الضريبة. وسيقوم بإرسال (POST) إلى الخادم معلمات بالشكل التالي:
params=$marié,$enfants,$salaire حيث
- $marié هي السلسلة oui أو non،
- و$enfants هو عدد الأطفال،
- $salaire هو راتب المكلف
يجد المعلمات الثلاثة السابقة في ملف نصي [data.txt] بالشكل (متزوج، أطفال، راتب):
سيقوم البرنامج النصي للعميل
- سيقرأ الملف النصي [data.txt] سطراً سطراً
- وسيقوم بإرسال السلسلة params=$marié,$enfants,$salaire إلى خدمة الويب الخاصة بحساب الضريبة
- وسيسترد الرد من الخدمة. وقد يتخذ هذا الرد شكلين:
- سيتم تسجيل استجابة الخادم في ملف نصي [resultats.txt] بأحد الشكلين التاليين:
رمز البرنامج النصي للعميل هو كما يلي:
<?php
// عميل الضرائب
// إدارة الأخطاء
ini_set("display_errors", "off");
// ---------------------------------------------------------------------------------
// فئة من وظائف المساعدة
class Utilitaires {
function cutNewLinechar($ligne) {
...
}
}
// الرئيسية -----------------------------------------------------
// تعريف الثوابت
$DATA = "data.txt";
$RESULTATS = "resultats.txt";
// بيانات الخادم
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/impots_05_web.php";
// معلمات الأشخاص الخاضعين للضريبة (الحالة الاجتماعية، عدد الأبناء، الراتب السنوي)
// تم وضعها في ملف نصي $DATA بواقع سطر واحد لكل دافع ضرائب
// النتائج (الحالة الاجتماعية، عدد الأطفال، الراتب السنوي، الضريبة المستحقة)
// أو (الحالة الاجتماعية، عدد الأطفال، الراتب السنوي، رسالة الخطأ) تم وضعها في
// الملف النصي $RESULTATS بمعدل نتيجة واحدة في كل سطر
// فئة الأدوات المساعدة
$u = new Utilitaires();
// فتح ملف بيانات دافعي الضرائب
$data = fopen($DATA, "r");
if (!$data) {
print "Impossible d'ouvrir en lecture le fichier des données [$DATA]\n";
exit;
}
// فتح ملف النتائج
$résultats = fopen($RESULTATS, "w");
if (!$résultats) {
print "Impossible de créer le fichier des résultats [$RESULTATS]\n";
exit;
}
// معالجة السطر الحالي من ملف بيانات دافعي الضرائب
while ($ligne = fgets($data, 100)) {
// إزالة علامة نهاية السطر إن وجدت
$ligne = $u->cutNewLineChar($ligne);
// استرجاع الحقول الثلاثة متزوج:أطفال:راتب التي تشكل $ligne
list($marié, $enfants, $salaire) = explode(",", $ligne);
// يتم حساب الضريبة
list($erreur, $impôt) = calculerImpot($HOTE, $PORT, $urlServeur, $cookie, array($marié, $enfants, $salaire));
// يتم تسجيل النتيجة
$résultat = $erreur ? "$marié:$enfants:$salaire:$erreur" : "$marié:$enfants:$salaire:$impôt";
fputs($résultats, "$résultat\n");
// البيانات التالية
}
// يتم إغلاق الملفات
fclose($data);
fclose($résultats);
// النهاية
print "Terminé...\n";
exit;
function calculerImpot($HOTE, $PORT, $urlServeur, &$cookie, $params) {
// يتم توصيل العميل بـ ($HOTE،$PORT،$urlServeur)
//ملف تعريف الارتباط $cookie إذا كان غير فارغ. يتم تمرير $cookie كمرجع
// يرسل ملف تعريف الارتباط $params إلى الخادم
// يستفيد من السطر الوحيد الذي أرسله الخادم
// فتح اتصال على المنفذ 80 لـ $HOTE
$connexion = fsockopen($HOTE, $PORT);
// خطأ؟
if (!$connexion)
return array("erreur lors de la connexion au serveur ($HOTE, $PORT)");
// يجب أن تنتهي رؤوس (headers) بروتوكول HTTP بسطر فارغ
// POST
fputs($connexion, "POST $urlServeur HTTP/1.1\n");
// المضيف
fputs($connexion, "Host: localhost\n");
// الاتصال
fputs($connexion, "Connection: close\n");
// يتم إرسال ملف تعريف الارتباط إذا لم يكن فارغًا
if ($cookie) {
fputs($connexion, "Cookie: $cookie\n");
}//if
// يتم الآن إرسال تعليمات العميل بعد ترميزها
$infos = "params=" . urlencode(implode(",", $params));
// يتم تحديد نوع المعلومات التي سيتم إرسالها
fputs($connexion, "Content-type: application/x-www-form-urlencoded\n");
// يتم إرسال حجم (عدد الأحرف) المعلومات التي سيتم إرسالها
fputs($connexion, "Content-length: " . strlen($infos) . "\n");
// نرسل سطراً فارغاً
fputs($connexion, "\n");
// يتم إرسال المعلومات
fputs($connexion, $infos);
// يتم عرض استجابة خادم الويب
// مع الحرص على استرداد ملف تعريف الارتباط (cookie) إن وجد
while ($ligne = fgets($connexion, 1000)) {
// ملف تعريف الارتباط - فقط عند الرد الأول
if (!$cookie) {
if (preg_match("/^Set-Cookie: (.*?)\s*$/", $ligne, $champs)) {
$cookie = $champs[1];
}//if
}
// بمجرد ظهور سطر فارغ، تكون الاستجابة HTTP قد اكتملت
if (trim($ligne) == "") {
break;
}
}//while
// قراءة سطر النتيجة
$ligne = fgets($connexion, 1000);
// يتم إغلاق الاتصال
fclose($connexion);
// حساب النتيجة
$erreur="";
$impôt="";
if (preg_match("/^<erreur>(.*?)<\/erreur>\s*$/", $ligne, $champs)) {
$erreur = $champs[1];
} else {
if (preg_match("/^<impot>(.*?)<\/impot>\s*$/", $ligne, $champs)) {
$impôt = $champs[1];
}else{
$erreur="résultat du serveur non exploitable";
}
}
// العودة
return array($erreur, $impôt);
}
تعليقات
يتضمن كود البرنامج النصي للعميل عناصر سبق تناولها:
- الأسطر 9-15: تم عرض الفئة [Utilitaires] في الإصدار 3 الفقرة 6
- الأسطر 17-68: البرنامج الرئيسي مشابه للبرنامج الوارد في الإصدار 1 الفقرة 4.2. ولا يختلف عنه إلا في حساب الضريبة، السطر 56.
- السطر 56: تقبل دالة حساب الضريبة المعلمات التالية:
- $HOTE، $PORT، $urlServeur: تتيح الاتصال بخدمة الويب
- $cookie: هو ملف تعريف الارتباط الخاص بالجلسة. يتم تمرير هذا المعامل بالرجوع. يتم تحديد قيمته بواسطة دالة حساب الضريبة. عند الاستدعاء الأول، لا يكون له قيمة. وبعد ذلك يكون له قيمة.
- array($marié, $enfants, $salaire): يمثل سطرًا من ملف [data.txt]
تُرجع دالة حساب الضريبة مصفوفة من نتيجتين ($erreur، $impôt) حيث $erreur هو رسالة خطأ محتملة و $impôt هو مبلغ الضريبة.
- الأسطر 70 – 134: نجد هنا عميل HTTP كلاسيكيًا مثلما رأينا الكثير منه. تجدر الإشارة إلى النقاط التالية:
- السطر 83: يتم إرسال المعلمات ($marié، $enfants، $salaire) إلى الخادم عبر POST
- الأسطر 89-91: إذا كان لدى العميل معرّف جلسة عمل، فإنه يرسله إلى الخادم
- السطر 93: إنشاء المعلمة params
- السطر 101: إرسال المعلمة params
- الأسطر 104-115: يقرأ العميل جميع رؤوس HTTP المرسلة من الخادم حتى يصل إلى السطر الفارغ الذي يشير إلى نهاية الرؤوس. ويستغل هذه الفرصة لاسترداد معرّف الجلسة عند تلقي الرد على طلبه الأول.
- الأسطر 123-125: يتم معالجة أي سطر من النوع <erreur>message</erreur>
- الأسطر 126-128: نفعل الشيء نفسه مع سطر محتمل بالصيغة <impot>montant</impot>
- السطر 133: يتم عرض النتيجة
11.2. خدمة الويب لحساب الضريبة
نحن مهتمون هنا بالبرامج النصية الثلاثة التي يتكون منها الخادم:
![]() |
مشروع NetBeans المقابل هو التالي:
![]() |
في [1]، يتكون الخادم من البرامج النصية PHP التالية:
- يحتوي [impots_05_entites] على الفئات المستخدمة من قبل الخادم
- يحتوي [impots_05_dao] على الفئات والواجهات الخاصة بطبقة [dao]
- يحتوي [impots_05_metier] على الفئات والواجهات الخاصة بطبقة [metier]
- يحتوي [impots_05_web] على الفئات والواجهات الخاصة بطبقة [dao]
نبدأ بعرض فئتين تستخدمهما الطبقات المختلفة لخدمة الويب.
11.2.1. كيانات خدمة الويب (impots_05_entites)
تحتوي قاعدة البيانات MySQL [dbimpots] على جدول [impots] الذي يحتوي على البيانات اللازمة لحساب الضريبة [1]:
![]() |
سنقوم بتخزين بيانات الجدول MySQL [impots] في مصفوفة كائنات Tranche حيث Tranche هي الفئة التالية:
<?php
// شريحة ضريبية
class Tranche {
// الحقول الخاصة
private $limite;
private $coeffR;
private $coeffN;
// مُستردات ومُعيّنات
public function getLimite() {
return $this->limite;
}
public function setLimite($limite) {
$this->limite = $limite;
}
public function getCoeffR() {
return $this->coeffR;
}
public function setCoeffR($coeffR) {
$this->coeffR = $coeffR;
}
public function getCoeffN() {
return $this->coeffN;
}
public function setCoeffN($coeffN) {
$this->coeffN = $coeffN;
}
// منشئ
public function __construct($limite, $coeffR, $coeffN) {
$this->setLimite($limite);
$this->setCoeffR($coeffR);
$this->setCoeffN($coeffN);
}
// toString
public function __toString(){
return "[$this->limite,$this->coeffR,$this->coeffN]";
}
}
ستُستخدم الحقول الخاصة [$limite, $coeffR, $coeffN] لتخزين أعمدة [limites, coeffR, coeffN] لصف واحد من الجدول MySQL [impots].
بالإضافة إلى ذلك، سيستخدم كود الخادم استثناءً خاصًا به، وهو الفئة ImpotsException:
- السطر 1: الفئة [ImpotsException] مشتقة من الفئة [Exception] المُعرَّفة مسبقًا في PHP 5
- السطر 3: يقبل منشئ الفئة [ImpotsException] معلمتين:
- $message: رسالة خطأ
- $code: رمز خطأ
11.2.2. الطبقة [dao] (impots_05_dao)
تضمن الطبقة [dao] الوصول إلى بيانات قاعدة البيانات:
![]() |
تقدم الطبقة [dao] الواجهة التالية:
لا تعرض واجهة IImpotsDao سوى الوظيفة getData. تقوم هذه الوظيفة بوضع الأسطر المختلفة من الجدول MySQL [dbimpots.impots] في مصفوفة من الكائنات Tranche.
فئة التنفيذ هي كما يلي:
<?php
// طبقة Dao
// التبعيات
require_once "impots_05_entites.php";
// الثوابت
define("TABLE", "impots");
// -----------------------------------------------------------------
// التنفيذ المجرد
abstract class ImpotsDaoWithPdo implements IImpotsDao {
// الحقول الخاصة
private $dsn;
private $user;
private $passwd;
private $tranches;
// مُستردات ومُعيّنات
public function getDsn() {
return $this->dsn;
}
public function setDsn($dsn) {
$this->dsn = $dsn;
}
public function getUser() {
return $this->user;
}
public function setUser($user) {
$this->user = $user;
}
public function getPasswd() {
return $this->passwd;
}
public function setPasswd($passwd) {
$this->passwd = $passwd;
}
// منشئ
public function __construct($dsn, $user, $passwd) {
// تسجيل المعلمات
$this->setDsn($dsn);
$this->setUser($user);
$this->setPasswd($passwd);
// استرداد البيانات من SGBD
// يتم توصيل ($user,$pwd) بقاعدة البيانات $dsn
try {
// الربط
$connexion = new PDO($dsn, $user, $passwd, array(PDO::ATTR_PERSISTENT => true));
// قراءة الجدول $TABLE
$requête = "select limites,coeffR,coeffN from " . TABLE;
// تنفيذ الاستعلام $requête على الاتصال $connexion
$statement = $connexion->prepare($requête);
$statement->execute();
// استخدام نتيجة الاستعلام
while ($colonnes = $statement->fetch()) {
$this->tranches[] = new Tranche($colonnes[0], $colonnes[1], $colonnes[2]);
}
// إنهاء الاتصال
$connexion=NULL;
} catch (PDOException $e) {
// العودة مع خطأ
throw new ImpotsException($e->getMessage(), 1);
}
}
public function getData(){
return $this->tranches;
}
}
- السطر 5: يحتاج تنفيذ واجهة [IImpotsDao] إلى الفئات المحددة في البرنامج النصي [impots_05_entites].
- السطر 11: تعريف فئة مجردة. الفئة المجردة هي فئة لا يمكن إنشاء مثيل لها. يجب أن تكون الفئة المجردة مشتقة حتى يمكن إنشاء مثيل لها. يمكن إعلان فئة ما على أنها مجردة لأنه لا يمكن إنشاء مثيل لها (بعض أساليبها غير محددة) أو لأننا لا نريد إنشاء مثيل لها. هنا، لا نريد إنشاء مثيل للفئة [ImpotsDaoWithPdo]. سنقوم بإنشاء مثيلات للفئات المشتقة.
- السطر 11: تنفذ الفئة [ImpotsDaoWithPdo] الواجهة [IImpotsDao]. لذا يجب أن تحدد الطريقة getData. توجد هذه الطريقة في الأسطر 72-74.
- السطر 14: $dsn (اسم مصدر البيانات) هو سلسلة أحرف تحدد بشكل فريد SGBD وقاعدة البيانات المستخدمة.
- السطر 15: $user يحدد هوية المستخدم الذي يتصل بقاعدة البيانات
- السطر 16: $passwd هي كلمة مرور المستخدم السابق
- السطر 17: $tranches هو مصفوفة الكائنات Tranche التي سيتم فيها تخزين الجدول MySQL [dbimpots.impots].
- الأسطر 45-70: منشئ الفئة. سبق أن تناولنا هذا الكود في الإصدار 4، الفقرة 8.2. تجدر الإشارة إلى أن إنشاء الكائن [ImpotsDaoWithPdo] قد يفشل. وعندئذ يتم إطلاق استثناء من النوع [ImpotsException].
- الأسطر 72-74: الطريقة [getData] للواجهة [IImpotsDao].
تتوافق الفئة [ImpotsDaoWithPdo] مع أي SGBD. يتطلب منشئ الفئة، في السطر 45، معرفة اسم مصدر البيانات (Data Source Name) لقاعدة البيانات. تعتمد هذه السلسلة من الأحرف على SGBD المستخدم. وقد تم اختيار عدم إلزام مستخدم الفئة بمعرفة اسم مصدر البيانات هذا. لكل SGBD، ستكون هناك فئة خاصة مشتقة من [ImpotsDaoWithPdo]. بالنسبة لـ SGBD و MySQL، ستكون الفئة التالية:
class ImpotsDaoWithMySQL extends ImpotsDaoWithPdo {
public function __construct($host, $port, $base, $user, $passwd) {
parent::__construct("mysql:host=$host;dbname=$base;port=$port", $user, $passwd);
}
}
- في السطر 3، لا يطلب المطور اسم مصدر البيانات (Data Source Name)، بل يطلب فقط اسم الجهاز المضيف لـ SGBD ($host)، ومنفذ الاستماع الخاص به ($port)، واسم قاعدة البيانات ($base).
- السطر 4، يتم إنشاء اسم مصدر البيانات (Data Source Name) لقاعدة البيانات MySQL واستخدامه لاستدعاء منشئ الفئة الأم.
تجدر الإشارة إلى أنه للتكيف مع SGBD آخر، يكفي كتابة الفئة المشتقة من [ImpotsDaoWithPdo] المناسبة. يتعلق الأمر في كل مرة بإنشاء اسم مصدر البيانات الخاص بـ SGBD المستخدم.
11.2.3. الطبقة [métier] (impots_05_metier)
تحتوي الطبقة [metier] على منطق حساب الضريبة:
![]() |
تقدم الطبقة [métier] الواجهة التالية:
<?php
// واجهة الأعمال
interface IImpotsMetier {
public function calculerImpot($marié, $enfants, $salaire);
}
لا تعرض الواجهة [IImpotsMetier] سوى طريقة واحدة، وهي الطريقة [calculerImpot] التي تسمح بحساب ضريبة المكلف بناءً على المعلمات التالية:
- $marié: سلسلة نعم/لا حسب ما إذا كان المكلف متزوجًا أم لا
- $enfants: عدد أطفال المكلف
- $salaire: راتبه
وستزودها الطبقة [web] بهذه المعلمات.
تنفيذ واجهة [IImpotsMetier] هو كما يلي:
// التبعيات
require_once "impots_05_dao.php";
// ------------------------------------------------------------------
// فئة التنفيذ
class ImpotsMetier implements IImpotsMetier {
// طبقة DAO
private $dao;
// مصفوفة الكائنات [Tranche]
private $data;
// الوصول والتعيين
public function getDao() {
return $this->dao;
}
public function setDao($dao) {
$this->dao = $dao;
}
public function setData($data){
$this->data=$data;
}
public function __construct($dao) {
// يتم استرداد البيانات اللازمة لحساب الضريبة
$this->setDao($dao);
$this->setData($this->dao->getData());
}
public function calculerImpot($marié, $enfants, $salaire) {
// $marié: نعم، لا
// $enfants: عدد الأطفال
// $salaire: الراتب السنوي
// عدد الحصص
$marié = strtolower($marié);
if ($marié == "oui")
$nbParts = $enfants / 2 + 2;
else
$nbParts=$enfants / 2 + 1;
// نصف حصة إضافية إذا كان عدد الأطفال 3 على الأقل
if ($enfants >= 3)
$nbParts+=0.5;
// الدخل الخاضع للضريبة
$revenuImposable = 0.72 * $salaire;
// معدل الأسرة
$quotient = $revenuImposable / $nbParts;
// يتم وضعه في نهاية جدول الحدود لإنهاء الحلقة التالية
$N = count($this->data);
$this->data[$N - 1]->setLimite($quotient);
// حساب الضريبة
$i = 0;
while ($i < $N and $quotient > $this->data[$i]->getLimite()) {
$i++;
}
// نظرًا لوضع $quotient في نهاية الجدول $limites، فإن الحلقة السابقة
// لا يمكن أن تتجاوز الجدول $limites
// والآن يمكننا حساب الضريبة
return floor($revenuImposable * $this->data[$i]->getCoeffR() - $nbParts * $this->data[$i]->getCoeffN());
}
}
- السطر 2: تحتاج الطبقة [métier] إلى فئات الطبقة [dao] والكيانات (Tranche، ImpotsException).
- السطر 6: الفئة [ImpotsMetier] تنفذ واجهة [IimpotsMetier].
- الأسطر 9-11: الحقول الخاصة بالفئة:
- $dao: مرجع إلى الطبقة [dao]
- $data: مصفوفة من الكائنات من النوع [Tranche] مقدمة من الطبقة [dao]
- الأسطر 26-30: يقوم منشئ الفئة بتهيئة الحقلين السابقين. ويتلقى كمعلمة مرجعًا إلى الطبقة [dao].
- الأسطر 32-61: تنفيذ الطريقة [calculerImpot] الخاصة بالواجهة [IimpotsMetier]. وقد ظهرت هذه الطريقة منذ الإصدار 1 (الفقرة 4.2).
11.2.4. الطبقة [web] (impots_05_web)
تحتوي الطبقة [metier] على منطق حساب الضريبة:
![]() |
تتكون الطبقة [web] من خدمة الويب التي تستجيب لعملاء الويب. تجدر الإشارة إلى أن هؤلاء يقدمون طلبًا إلى خدمة الويب عن طريق إرسال المعلمة التالية: params=marié,enfants,salaire. نحن نتعامل مع خدمة ويب مثل تلك التي تمكنا من إنشائها في الفقرات السابقة. رمزها هو التالي:
<?php
// طبقة الأعمال
require_once "impots_05_metier.php";
// إدارة الأخطاء
ini_set("display_errors", "off");
// رأس الصفحة UTF-8
header("Content-Type: text/plain; charset=utf-8");
// ------------------------------------------------------------------------------
// خدمة الويب الخاصة بالضرائب
// تعريف الثوابت
$HOTE = "localhost";
$PORT = 3306;
$BASE = "dbimpots";
$USER = "root";
$PWD = "";
// تم وضع البيانات اللازمة لحساب الضريبة في جدول mysql IMPOTS
// التابعة لقاعدة البيانات $BASE. الجدول له البنية التالية
// حدود decimal(10,2)، coeffR decimal(6,2)، coeffN decimal(10,2)
// معلمات الأشخاص الخاضعين للضريبة (الحالة الاجتماعية، عدد الأبناء، الراتب السنوي)
// يتم إرسالها من قبل العميل بالصيغة params=الحالة الاجتماعية، عدد الأبناء، الراتب السنوي
// يتم إرسال النتائج (الحالة الاجتماعية، عدد الأطفال، الراتب السنوي، الضريبة المستحقة) إلى العميل
// في شكل <ضريبة>ضريبة</ضريبة>
// أو بالصيغة <خطأ>خطأ</خطأ>، إذا كانت المعلمات غير صالحة
// يتم استرداد الطبقة [métier] في الجلسة
session_start();
if (!isset($_SESSION['metier'])) {
// إنشاء مثيل لطبقة [dao] وطبقة [métier]
try {
$_SESSION['metier'] = new ImpotsMetier(new ImpotsDaoWithMySQL($HOTE, $PORT, $BASE, $USER, $PWD));
} catch (ImpotsException $ie) {
print "<erreur>Erreur : " . utf8_encode($ie->getMessage() . "</erreur>");
exit;
}
}
$metier = $_SESSION['metier'];
// يتم استرداد السطر المرسل من العميل
$params = utf8_encode(htmlspecialchars(strtolower(trim($_POST['params']))));
$items = explode(",", $params);
// يجب ألا يكون هناك سوى 3 معلمات
if (count($items) != 3) {
print "<erreur>[$params] : nombre de paramètres invalides</erreur>\n";
exit;
}//if
// يجب أن تكون المعلمة الأولى (الحالة الاجتماعية) نعم/لا
$marié = trim($items[0]);
if ($marié != "oui" and $marié != "non") {
print "<erreur>[$params] : 1er paramètre invalide</erreur>\n";
exit;
}//إذا
// يجب أن يكون المعامل الثاني (عدد الأطفال) عددًا صحيحًا
if (!preg_match("/^\s*(\d+)\s*$/", $items[1], $champs)) {
print "<erreur>[$params] : 2ième paramètre invalide</erreur>\n";
exit;
}//إذا
$enfants = $champs[1];
// يجب أن يكون المعامل الثالث (الراتب) عددًا صحيحًا
if (!preg_match("/^\s*(\d+)\s*$/", $items[2], $champs)) {
print "<erreur>[$params] : 3ième paramètre invalide</erreur>\n";
exit;
}//إذا
$salaire = $champs[1];
// يتم حساب الضريبة
$impôt = $metier->calculerImpot($marié, $enfants, $salaire);
// يتم إرجاع النتيجة
print "<impot>$impôt</impot>\n";
// النهاية
exit;
- السطر 4: تحتاج الطبقة [web] إلى فئات الطبقة [métier]
- الأسطر 30-40: يتم وضع المرجع على الطبقة [métier] في الجلسة. وإذا ما تذكرنا أن هذه الطبقة [métier] تحتوي على مرجع إلى الطبقة [dao]، وأن هذه الأخيرة تخزن بيانات الطبقة SGBD، فإننا ندرك عندئذٍ ما يلي:
- أن الطلب الأول للعميل سيؤدي إلى الوصول إلى SGBD
- أن الطلبات التالية من نفس العميل ستستخدم البيانات المخزنة في الطبقة [dao]. وبالتالي، لن يكون هناك وصول إلى SGBD.
- السطر 34: إنشاء طبقة [métier] تعمل مع طبقة [dao] التي تم تنفيذها لـ SGBD MySQL
- الأسطر 35-37: معالجة أي خطأ محتمل في العملية السابقة. في هذه الحالة، يتم إرسال سطر <erreur>message</erreur> إلى العميل.
- السطر 43: يتم استرداد المعلمة 'params' التي أرسلها العميل.
- الأسطر 46-49: يتم التحقق من عدد المعلومات الموجودة في «params»
- الأسطر 51-55: يتم التحقق من صحة المعلومة الأولى
- الأسطر 56-60: نفس الإجراء بالنسبة للمعلومة الثانية
- الأسطر 62-66: نفس الإجراء بالنسبة للمعلومة الثالثة
- السطر 69: تقوم الطبقة [métier] بحساب الضريبة.
- السطر 71: إرسال النتيجة إلى العميل
النتائج
نتذكر أن العميل [client_impots_web_05] يستخدم الملف [data.txt] التالي:
انطلاقًا من هذه الأسطر (متزوج، أطفال، راتب)، يستعلم العميل عن خادم حساب الضرائب ويسجل النتائج في ملف نصي [resultats.txt]. بعد تشغيل العميل، يكون محتوى هذا الملف كما يلي:
oui:2:200000:22504
non:2:200000:33388
oui:3:200000:16400
non:3:200000:22504
oui:5:50000:0
non:0:3000000:1354938
حيث يكون كل سطر على الشكل التالي (المتزوج، الأبناء، الراتب، الضريبة المحسوبة).






