4. تطبيق توضيحي
نقترح توضيح الطريقة السابقة بمثال لحساب الضريبة.
4.1. المشكلة
نهدف إلى كتابة برنامج لحساب ضريبة دافع الضرائب. نأخذ في الاعتبار الحالة المبسطة لدافع الضرائب الذي لا يملك سوى راتبه ليبلغ عنه:
- نحسب عدد شرائح الضريبة للموظف: nbParts = nbEnfants / 2 + 1 إذا كان غير متزوج، و nbEnfants / 2 + 2 إذا كان متزوجًا، حيث nbEnfants هو عدد الأطفال. يزداد عدد الشرائح بمقدار 0.5 إذا كان هناك ثلاثة أطفال أو أكثر.
- نحسب دخله الخاضع للضريبة R = 0.72 * S، حيث S هو راتبه السنوي
- نحسب معامل الأسرة Q = R/N
- نحسب ضريبته I بناءً على البيانات التالية
| يحتوي كل صف على 3 حقول: limit و coeffR و coeffN. لحساب الضريبة I، ابحث عن الصف الأول الذي يكون فيه QF <= limit. على سبيل المثال، إذا كان QF = 30000، فإن الصف الذي تم العثور عليه هو: 31810 0 3309.5 تكون الضريبة I عندئذٍ مساوية لـ 0.2*R - 3309.5*nbParts. إذا كان QF بحيث لا يتم استيفاء الشرط QF <= limit أبدًا، يتم استخدام المعاملات من الصف الأخير: 0 0.65 49062، مما يعطي الضريبة I = 0.65*R - 49062*nbParts. |
4.2. قاعدة البيانات
يتم تخزين البيانات السابقة في قاعدة بيانات MySQL باسم dbimpots. يتمتع المستخدم seldbimpots بكلمة المرور mdpseldbimpots بحق الوصول للقراءة فقط إلى محتويات قاعدة البيانات. تحتوي قاعدة البيانات على جدول واحد باسم impots بالهيكل التالي:

ومحتوياتها هي كما يلي:

4.3. بنية MVC للتطبيق
سيكون للتطبيق بنية MVC التالية:
![]() |
- سيكون وحدة التحكم main.php هي وحدة التحكم العامة الموصوفة أعلاه
- يتم إرسال طلب العميل إلى وحدة التحكم في شكل استعلام بالصيغة main.php?action=xx. تحدد قيمة معلمة الإجراء البرنامج النصي الذي سيتم تنفيذه في كتلة ACTIONS. يعيد البرنامج النصي للإجراء الذي تم تنفيذه متغيرًا إلى وحدة التحكم يشير إلى الحالة التي يجب أن يوضع فيها تطبيق الويب. وبناءً على هذه الحالة، ستقوم وحدة التحكم بتنشيط أحد مولدات العرض لإرسال الاستجابة إلى العميل.
- impots-data.php هي الفئة المسؤولة عن تزويد وحدة التحكم بالبيانات التي تحتاجها
- impots-calcul.php هي فئة منطق الأعمال التي تحسب الضريبة
4.4. فئة الوصول إلى البيانات
تم تصميم فئة الوصول إلى البيانات لإخفاء مصدر البيانات عن تطبيق الويب. تتضمن واجهتها طريقة getData التي تُرجع مصفوفات البيانات الثلاثة اللازمة لحساب الضريبة. في مثالنا، يتم استرداد البيانات من قاعدة بيانات MySQL. لجعل الفئة مستقلة عن نوع نظام إدارة قواعد البيانات الفعلي، سنستخدم مكتبة pear::DB الموضحة في الملحق. رمز الفئة هو كما يلي:
<?php
// libraries
require_once 'DB.php';
class impots_data{
// data source access class DBIMPOTS
// attributes
var $sDSN; // the connection chain
var $sDatabase; // base name
var $oDB; // base connection
var $aErreurs; // error list
var $oRésultats; // query result
var $connecté; // boolean indicating whether or not you are connected to the database
var $sQuery ; // the last query executed
// manufacturer
function impots_data($dDSN){
// $dDSN: dictionary defining the link to be established
// $dDSN['sgbd']: type of SGBD to be connected to
// $dDSN['host']: name of the host machine hosting it
// $dDSN['database']: name of the database to be connected to
// $dDSN['user']: a bse user
// $dDSN['mdp']: its password
// creates in $oDB a connection to the database defined by $dDSN as $dDSN['user']
// if the connection is successful
// sets $sDSN to the database connection string
// sets $sDataBase to the name of the database to which you are connecting
// sets $connecté to true
// if the connection fails
// puts the appropriate error msg in the $aErreurs list
// closes the connection if necessary
// sets $connecté to false
// raz error list
$this->aErreurs=array();
// create a connection to the $sDSN database
$this->sDSN=$dDSN["sgbd"]."://".$dDSN["user"].":".$dDSN["mdp"]."@".$dDSN["host"]."/".$dDSN["database"];
$this->sDatabase=$dDSN["database"];
$this->connect();
// connected?
if( ! $this->connecté) return;
// the connection was successful
$this->connecté=TRUE;
}//manufacturer
// ------------------------------------------------------------------
function connect(){
// (re)connecting to the base
// raz error list
$this->aErreurs=array();
// create a connection to the $sDSN database
$this->oDB=DB::connect($this->sDSN,true);
// mistake?
if(DB::iserror($this->oDB)){
// on note l'erreur
$this->aErreurs[]="Echec de la connexion à la base [".$this->sDatabase."] : [".$this->oDB->getMessage()."]";
// connection failed
$this->connecté=FALSE;
// end
return;
}
// we're connected
$this->connecté=TRUE;
}//connect
// ------------------------------------------------------------------
function disconnect(){
// if connected, closes the connection to the $sDSN database
if($this->connecté){
$this->oDB->disconnect();
// we are disconnected
$this->connecté=FALSE;
}//if
}//disconnect
// -------------------------------------------------------------------
function execute($sQuery){
// $sQuery: query to be executed
// memorize the request
$this->sQuery=$sQuery;
// are we connected?
if(! $this->connecté){
// on note l'erreur
$this->aErreurs[]="Pas de connexion existante à la base [$this->sDatabase]";
// end
return;
}//if
// query execution
$this->oRésultats=$this->oDB->query($sQuery);
// mistake?
if(DB::iserror($this->oRésultats)){
// on note l'erreur
$this->aErreurs[]="Echec de la requête [$sQuery] : [".$this->oRésultats->getMessage()."]";
// return
return;
}//if
}//execute
// ------------------------------------------------------------------
function getData(){
// we retrieve the 3 limit data series, coeffr, coeffn
$this->execute('select limites, coeffR, coeffN from impots');
// mistakes?
if(count($this->aErreurs)!=0) return array();
// browse the result of the select
while ($ligne = $this->oRésultats->fetchRow(DB_FETCHMODE_ASSOC)) {
$limites[]=$ligne['limites'];
$coeffr[]=$ligne['coeffR'];
$coeffn[]=$ligne['coeffN'];
}//while
return array($limites,$coeffr,$coeffn);
}//getDataImpots
}//class
?>
قد يبدو برنامج الاختبار كما يلي:
<?php
// library
require_once "c-impots-data.php";
require_once "DB.php";
// testing the impots-data class
ini_set('track_errors','on');
ini_set('display_errors','on');
// dbimpots base configuration
$dDSN=array(
"sgbd"=>"mysql",
"user"=>"seldbimpots",
"mdp"=>"mdpseldbimpots",
"host"=>"localhost",
"database"=>"dbimpots"
);
// opening of the session
$oImpots=new impots_data($dDSN);
// mistakes?
if(checkErreurs($oImpots)){
exit(0);
}
// follow-up
echo "Connecté à la base...\n";
// recovery of limit data, coeffr, coeffn
list($limites,$coeffr,$coeffn)=$oImpots->getData();
// mistakes?
if( ! checkErreurs($oImpots)){
// content
echo "données : \n";
for($i=0;$i<count($limites);$i++){
echo "[$limites[$i],$coeffr[$i],$coeffn[$i]]\n";
}//for
}//if
// disconnect
$oImpots->disconnect();
// follow-up
echo "Déconnecté de la base...\n";
// end
exit(0);
// ----------------------------------
function checkErreurs(&$oImpots){
// mistakes?
if(count($oImpots->aErreurs)!=0){
// display
for($i=0;$i<count($oImpots->aErreurs);$i++){
echo $oImpots->aErreurs[$i]."\n";
}//for
// errors
return true;
}//if
// no errors
return false;
}//checkErreurs
?>
يؤدي تشغيل برنامج الاختبار هذا إلى النتائج التالية:
Connecté à la base...
données :
[12620,0,0]
[13190,0.05,631]
[15640,0.1,1290.5]
[24740,0.15,2072.5]
[31810,0.2,3309.5]
[39970,0.25,4900]
[48360,0.3,6898]
[55790,0.35,9316.5]
[92970,0.4,12106]
[127860,0.45,16754]
[151250,0.5,23147.5]
[172040,0.55,30710]
[195000,0.6,39312]
[0,0.65,49062]
Déconnecté de la base...
4.5. فئة حساب الضريبة
تُستخدم هذه الفئة لحساب ضريبة دافع الضرائب. يتم توفير البيانات المطلوبة لهذا الحساب إلى مُنشئها. ثم تقوم بحساب الضريبة المقابلة. رمز الفئة هو كما يلي:
<?php
class impots_calcul{
// tax calculation class
// manufacturer
function impots_calcul(&$perso,&$data){
// $perso: dictionary with the following keys
// children : number of children
// salary: annual salary
// married: Boolean indicating whether the taxpayer is married or not
// impot(s): tax payable calculated by this manufacturer
// $data: dictionary with the following keys
// limits: tranche limits table
// coeffr: income coefficients table
// coefficients tablaeu of coefficients of number of shares
// the 3 arrays have the same number of elements
// calculating the number of shares
if($perso['marié'])
$nbParts=$perso['enfants']/2+2;
else $nbParts=$perso['enfants']/2+1;
if ($perso['enfants']>=3) $nbParts+=0.5;
// taxable income
$revenu=0.72*$perso['salaire'];
// family quotient
$QF=$revenu/$nbParts;
// search for tax bracket corresponding to QF
$nbTranches=count($data['limites']);
$i=0;
while($i<$nbTranches-2 && $QF>$data['limites'][$i]) $i++;
// tax
$perso['impot']=floor($data['coeffr'][$i]*$revenu-$data['coeffn'][$i]*$nbParts);
}//manufacturer
}//class
?>
قد يبدو برنامج الاختبار كما يلي:
<?php
// library
require_once "c-impots-data.php";
require_once "c-impots-calcul.php";
// dbimpots base configuration
$dDSN=array(
"sgbd"=>"mysql",
"user"=>"seldbimpots",
"mdp"=>"mdpseldbimpots",
"host"=>"localhost",
"database"=>"dbimpots"
);
// opening of the session
$oImpots=new impots_data($dDSN);
// mistakes?
if(checkErreurs($oImpots)){
exit(0);
}
// follow-up
echo "Connecté à la base...\n";
// recovery of limit data, coeffr, coeffn
list($limites,$coeffr,$coeffn)=$oImpots->getData();
// mistakes?
if(checkErreurs($oImpots)){
exit(0);
}
// we disconnect
$oImpots->disconnect();
// follow-up
echo "Déconnecté de la base...\n";
// tax calculation
$dData=array('limites'=>&$limites,'coeffr'=>&$coeffr,'coeffn'=>&$coeffn);
$dPerso=array('enfants'=>2,'salaire'=>200000,'marié'=>true,'impot'=>0);
new impots_calcul($dPerso,$dData);
dump($dPerso);
$dPerso=array('enfants'=>3,'salaire'=>200000,'marié'=>false,'impot'=>0);
new impots_calcul($dPerso,$dData);
dump($dPerso);
$dPerso=array('enfants'=>3,'salaire'=>20000,'marié'=>true,'impot'=>0);
new impots_calcul($dPerso,$dData);
dump($dPerso);
$dPerso=array('enfants'=>3,'salaire'=>2000000,'marié'=>true,'impot'=>0);
new impots_calcul($dPerso,$dData);
dump($dPerso);
// end
exit(0);
// ----------------------------------
function checkErreurs(&$oImpots){
// mistakes?
if(count($oImpots->aErreurs)!=0){
// display
for($i=0;$i<count($oImpots->aErreurs);$i++){
echo $oImpots->aErreurs[$i]."\n";
}//for
// errors
return true;
}//if
// no errors
return false;
}//checkErreurs
?>
يؤدي تشغيل برنامج الاختبار هذا إلى النتائج التالية:
Connecté à la base...
Déconnecté de la base...
[enfants,2] [salaire,200000] [marié,1] [impot,22506]
[enfants,3] [salaire,200000] [marié,] [impot,22506]
[enfants,3] [salaire,20000] [marié,1] [impot,0]
[enfants,3] [salaire,2000000] [marié,1] [impot,706752]
4.6. كيف يعمل التطبيق
عند تشغيل حاسبة الضرائب على الويب، تظهر طريقة العرض [v-form] التالية:
![]() |
يقوم المستخدم بملء الحقول ويطلب حساب الضريبة:
![]() |
لاحظ أن النموذج يُعاد إنشاؤه في الحالة التي أرسله بها المستخدم، كما يعرض مبلغ الضريبة المستحقة. قد يرتكب المستخدم أخطاء في إدخال البيانات. يتم الإبلاغ عن هذه الأخطاء عبر صفحة خطأ، سنسميها عرض [v-errors].
![]() |
![]() |
يتيح رابط [العودة إلى نموذج الإدخال] للمستخدم العودة إلى النموذج كما تم إرساله.
![]() |
وأخيرًا، يعيد زر [مسح النموذج] النموذج إلى حالته الأولية، أي كما استلمه المستخدم عند الطلب الأولي.
4.7. العودة إلى بنية MVC للتطبيق
يتميز التطبيق بهيكلية MVC التالية:
![]() |
لقد وصفنا للتو الفئتين impots-data.php و impots-calcul.php. سنقوم الآن بوصف العناصر الأخرى للبنية.
4.8. وحدة التحكم في التطبيق
وحدة التحكم main.php الخاصة بالتطبيق هي تلك التي تم وصفها في الجزء الأول من هذا الفصل. وهي وحدة تحكم عامة مستقلة عن التطبيق.
<?php
// generic controller
// configurable reading
include 'config.php';
// including libraries
for($i=0;$i<count($dConfig['includes']);$i++){
include($dConfig['includes'][$i]);
}//for
// start or resume session
session_start();
$dSession=$_SESSION["session"];
if($dSession) $dSession=unserialize($dSession);
// retrieve the action to be taken
$sAction=$_GET['action'] ? strtolower($_GET['action']) : 'init';
$sAction=strtolower($_SERVER['REQUEST_METHOD']).":$sAction";
// is the sequence of actions normal?
if( ! enchainementOK($dConfig,$dSession,$sAction)){
// abnormal sequence
$sAction='enchainementinvalide';
}//if
// share processing
$scriptAction=$dConfig['actions'][$sAction] ?
$dConfig['actions'][$sAction]['url'] :
$dConfig['actions']['actionInvalide']['url'];
include $scriptAction;
// send response(view) to customer
$sEtat=$dSession['etat']['principal'];
$scriptVue=$dConfig['etats'][$sEtat]['vue'];
include $scriptVue;
// end of script - we shouldn't get there unless there's a bug
trace ("Erreur de configuration.");
trace("Action=[$sAction]");
trace("scriptAction=[$scriptAction]");
trace("Etat=[$sEtat]");
trace("scriptVue=[$scriptVue]");
trace ("Vérifiez que les script existent et que le script [$scriptVue] se termine par l'appel à finSession.");
exit(0);
// ---------------------------------------------------------------
function finSession(&$dConfig,&$dReponse,&$dSession){
// $dConfig: configuration dictionary
// $dSession: dictionary containing session info
// $dReponse: the dictionary of arguments for the response page
// session registration
if(isset($dSession)){
// put the query parameters in the session
$dSession['requete']=strtolower($_SERVER['REQUEST_METHOD'])=='get' ? $_GET :
strtolower($_SERVER['REQUEST_METHOD'])=='post' ? $_POST : array();
$_SESSION['session']=serialize($dSession);
session_write_close();
}else{
// no session
session_destroy();
}
// we present the answer
include $dConfig['vuesReponse'][$dReponse['vuereponse']]['url'];
// end of script
exit(0);
}//endsession
//--------------------------------------------------------------------
function enchainementOK(&$dConfig,&$dSession,$sAction){
// checks whether the current action is authorized with respect to the previous state
$etat=$dSession['etat']['principal'];
if(! isset($etat)) $etat='sansetat';
// check action
$actionsautorisees=$dConfig['etats'][$etat]['actionsautorisees'];
$autorise= ! isset($actionsautorisees) || in_array($sAction,$actionsautorisees);
return $autorise;
}
//--------------------------------------------------------------------
function dump($dInfos){
// displays an information dictionary
while(list($clé,$valeur)=each($dInfos)){
echo "[$clé,$valeur]<br>\n";
}//while
}//follow-up
//--------------------------------------------------------------------
function trace($msg){
echo $msg."<br>\n";
}//follow-up
?>
4.9. إجراءات تطبيق الويب
هناك أربعة إجراءات:
- get:init: هذا هو الإجراء الذي يتم تشغيله أثناء الطلب الأولي إلى وحدة التحكم بدون معلمات. يقوم بعرض طريقة عرض "النموذج" الفارغة.
- post:clearform: الإجراء الذي يتم تشغيله بواسطة زر [Clear Form]. يعرض عرض [v-form] الفارغ.
- post:calculateTax: إجراء يتم تشغيله بواسطة زر [حساب الضريبة]. ويؤدي إلى إنشاء عرض [v-form] مع مبلغ الضريبة المستحقة، أو عرض [v-errors].
- get:returnform: إجراء يتم تشغيله بواسطة رابط [العودة إلى نموذج الإدخال]. يعرض طريقة العرض [v-form] مملوءة مسبقًا بالبيانات غير الصحيحة.
يتم تكوين هذه الإجراءات على النحو التالي في ملف التكوين:
<?php
…
// configuration of application actions
$dConfig['actions']['get:init']=array('url'=>'a-init.php');
$dConfig['actions']['post:calculerimpot']=array('url'=>'a-calculimpot.php');
$dConfig['actions']['get:retourformulaire']=array('url'=>'a-retourformulaire.php');
$dConfig['actions']['post:effacerformulaire']=array('url'=>'a-init.php');
$dConfig['actions']['enchainementinvalide']=array('url'=>'a-enchainementinvalide.php');
$dConfig['actions']['actionInvalide']=array('url'=>'a-actioninvalide.php');
يرتبط كل إجراء بالبرنامج النصي المسؤول عن معالجته. سيؤدي كل إجراء إلى نقل تطبيق الويب إلى حالة محددة بواسطة العنصر $dSession['state']['main']. ويُقصد بهذه الحالة أن يتم حفظها في الجلسة. بالإضافة إلى ذلك، يخزن الإجراء في قاموس $dResponse المعلومات اللازمة لعرض العرض المرتبط بالحالة الجديدة التي سيكون عليها التطبيق.
4.10. حالات تطبيق الويب
هناك حالتان:
[e-form]: state in which the different variants of the [v-form] view are displayed.
[e-errors]: state in which the [v-errors] view is displayed.
الإجراءات المسموح بها في هذه الحالات هي كما يلي:
<?php
…
// application status configuration
$dConfig['etats']['formulaire']=array(
'actionsautorisees'=>array('post:calculerimpot','get:init','post:effacerformulaire'),
'vue'=>'e-formulaire.php');
$dConfig['etats']['erreurs']=array(
'actionsautorisees'=>array('get:retourformulaire','get:init'),
'vue'=>'e-erreurs.php');
$dConfig['etats']['sansetat']=array('actionsautorisees'=>array('get:init'));
في الحالة، تتوافق الإجراءات المسموح بها مع عناوين URL المستهدفة للروابط أو أزرار [submit] في العرض المرتبط بتلك الحالة. بالإضافة إلى ذلك، يُسمح دائمًا بالإجراء 'get:init'. وهذا يسمح للمستخدم باسترداد عنوان URL main.php من شريط عناوين URL في متصفحه وإعادة تحميله بغض النظر عن حالة التطبيق. وهذا نوع من إعادة الضبط "اليدوية". ولا توجد الحالة 'sansetat' إلا عند بدء تشغيل التطبيق.
ترتبط كل حالة من حالات التطبيق ببرنامج نصي مسؤول عن إنشاء العرض المرتبط بتلك الحالة:
- الحالة [e-form]: البرنامج النصي e-formulaire.php
- الحالة [e-errors]: البرنامج النصي e-errors.php
ستعرض حالة [e-form] طريقة العرض [v-form] مع بعض الاختلافات. في الواقع، يمكن عرض طريقة العرض [v-form] فارغة، أو مملوءة مسبقًا، أو مع مبلغ الضريبة. تحدد الإجراء الذي ينقل التطبيق إلى حالة [e-form] الحالة الرئيسية للتطبيق في المتغير $dSession['state']['main']. يستخدم وحدة التحكم هذه المعلومات فقط. في تطبيقنا، ستضيف الإجراء الذي يؤدي إلى حالة [e-form] معلومات إضافية إلى $dSession['state']['secondary'], مما يسمح لمولد الاستجابة بمعرفة ما إذا كان يجب إنشاء نموذج فارغ، أو نموذج مملوء مسبقًا، أو نموذج مع أو بدون مبلغ الضريبة. كان بإمكاننا اتباع نهج مختلف بافتراض وجود ثلاث حالات مختلفة وبالتالي ثلاثة مولدات عرض يجب كتابتها.
4.11. ملف التكوين config.php الخاص بالتطبيق الويب
<?php
// php configuration
ini_set("register_globals","off");
ini_set("display_errors","off");
ini_set("expose_php","off");
// list of modules to be included
$dConfig['includes']=array('c-impots-data.php','c-impots-calcul.php');
// application controller
$dConfig['webapp']=array('titre'=>"Calculez votre impôt");
// aplication view configuration
$dConfig['vuesReponse']['modele1']=array('url'=>'m-reponse.php');
$dConfig['vuesReponse']['modele2']=array('url'=>'m-reponse2.php');
$dConfig['vues']['formulaire']=array('url'=>'v-formulaire.php');
$dConfig['vues']['erreurs']=array('url'=>'v-erreurs.php');
$dConfig['vues']['formulaire2']=array('url'=>'v-formulaire2.php');
$dConfig['vues']['erreurs2']=array('url'=>'v-erreurs2.php');
$dConfig['vues']['bandeau']=array('url'=>'v-bandeau.php');
$dConfig['vues']['menu']=array('url'=>'v-menu.php');
$dConfig['style']['url']='style1.css';
// configuration of application actions
$dConfig['actions']['get:init']=array('url'=>'a-init.php');
$dConfig['actions']['post:calculerimpot']=array('url'=>'a-calculimpot.php');
$dConfig['actions']['get:retourformulaire']=array('url'=>'a-retourformulaire.php');
$dConfig['actions']['post:effacerformulaire']=array('url'=>'a-init.php');
$dConfig['actions']['enchainementinvalide']=array('url'=>'a-enchainementinvalide.php');
$dConfig['actions']['actionInvalide']=array('url'=>'a-actioninvalide.php');
// application status configuration
$dConfig['etats']['e-formulaire']=array(
'actionsautorisees'=>array('post:calculerimpot','get:init','post:effacerformulaire'),
'vue'=>'e-formulaire.php');
$dConfig['etats']['e-erreurs']=array(
'actionsautorisees'=>array('get:retourformulaire','get:init'),
'vue'=>'e-erreurs.php');
$dConfig['etats']['sansetat']=array('actionsautorisees'=>array('get:init'));
// model application configuration
$dConfig["DSN"]=array(
"sgbd"=>"mysql",
"user"=>"seldbimpots",
"mdp"=>"mdpseldbimpots",
"host"=>"localhost",
"database"=>"dbimpots"
);
?>
4.12. إجراءات تطبيق الويب
4.12.1. التشغيل العام لنصوص الإجراءات
- يتم استدعاء نصوص الإجراءات من قبل وحدة التحكم بناءً على معلمة الإجراء التي تلقتها من العميل.
- بعد التنفيذ، يجب أن يبلغ البرنامج النصي للعمل وحدة التحكم بالحالة التي يجب وضع التطبيق فيها. يجب تحديد هذه الحالة في $dSession['etat']['principal'].
- قد يرغب برنامج نصي الإجراء في تخزين المعلومات في الجلسة. ويقوم بذلك عن طريق وضع هذه المعلومات في قاموس $dSession، الذي يتم حفظه تلقائيًا في الجلسة بواسطة وحدة التحكم في نهاية دورة الطلب والاستجابة.
- قد يحتوي نص برمجي الإجراء على معلومات يجب تمريرها إلى العروض. ويعد هذا الجانب مستقلاً عن وحدة التحكم. فهو يمثل الواجهة بين الإجراءات والعروض، وهي واجهة خاصة بكل تطبيق. وفي المثال الذي نناقشه هنا، ستزود الإجراءات مولِّدات العروض بالمعلومات عبر قاموس يُسمى $dResponse.
4.12.2. إجراء get:init
هذا هو الإجراء الذي يولد النموذج الفارغ. يوضح ملف التكوين أنه سيتم التعامل معه بواسطة البرنامج النصي a-init.php:
فيما يلي كود البرنامج النصي a-init.php:
<?php
// the input form is displayed
$dSession['etat']=array('principal'=>'e-formulaire', 'secondaire'=>'init');
?>
يحدد هذا البرنامج النصي ببساطة الحالة التي يجب أن يكون عليها التطبيق — حالة [e-form] — في $dSession['etat']['principal'], ويقدم وصفًا لهذه الحالة في $dSession['etat']['secondaire']. يوضح لنا ملف التكوين أن وحدة التحكم ستنفذ البرنامج النصي e-formulaire.php لتوليد الاستجابة للعميل.
<?php
…
$dConfig['etats']['e-formulaire']=array(
'actionsautorisees'=>array('post:calculerimpot','get:init','post:effacerformulaire'),
'vue'=>'e-formulaire.php');
سيقوم البرنامج النصي e-formulaire.php بإنشاء عرض [v-formulaire] في متغيره [init]، أي النموذج الفارغ.
4.12.3. الإجراء post:calculateTax
هذه هي الإجراء الذي يحسب الضريبة بناءً على البيانات التي تم إدخالها في النموذج. يحدد ملف التكوين أن البرنامج النصي a-calculimpot.php سيتولى هذا الإجراء:
فيما يلي كود البرنامج النصي a-calculimpot.php:
<?php
// tax calculation request
// we first check the validity of the parameters
$sOptMarie=$_POST['optmarie'];
if($sOptMarie!='oui' && $sOptMarie!='non'){
$erreurs[]="L'état marital [$sOptMarie] est erroné";
}
$sEnfants=trim($_POST['txtenfants']);
if(! preg_match('/^\d{1,3}$/',$sEnfants)){
$erreurs[]="Le nombre d'enfants [$sEnfants] est erroné";
}
$sSalaire=trim($_POST['txtsalaire']);
if(! preg_match('/^\d+$/',$sSalaire)){
$erreurs[]="Le salaire annuel [$sSalaire] est erroné";
}
// if there are mistakes, it's over
if(count($erreurs)!=0){
// preparing the error page
$dReponse['erreurs']=&$erreurs;
$dSession['etat']=array('principal'=>'e-erreurs','secondaire'=>'saisie');
return;
}//if
// the data entered is correct
// retrieve the data needed to calculate taxes
if(! $dSession['limites']){
// the data is not in the session
// we retrieve them from the data source
list($erreurs,$limites,$coeffr,$coeffn)=getData($dConfig['DSN']);
// if there are errors, the error page is displayed
if(count($erreurs)!=0){
// preparing the error page
$dReponse['erreurs']=&$erreurs;
$dSession['etat']=array('principal'=>'e-erreurs','secondaire'=>'database');
return;
}//if
// no errors - data put into session
$dSession['limites']=&$limites;
$dSession['coeffr']=&$coeffr;
$dSession['coeffn']=&$coeffn;
}//if
// here you have the data you need to calculate your taxes
// on calcule celui-ci
$dData=array('limites'=>&$dSession['limites'],
'coeffr'=>&$dSession['coeffr'],
'coeffn'=>&$dSession['coeffn']);
$dPerso=array('enfants'=>$sEnfants,'salaire'=>$sSalaire,'marié'=>($sOptMarie=='oui'),'impot'=>0);
new impots_calcul($dPerso,$dData);
// preparing the answer page
$dSession['etat']=array('principal'=>'e-formulaire','secondaire'=>'calculimpot');
$dReponse['impot']=$dPerso['impot'];
return;
//-----------------------------------------------------------------------
function getData($dDSN){
// connection to the data source defined by the $dDSN dictionary
$oImpots=new impots_data($dDSN);
if(count($oImpots->aErreurs)!=0) return array($oImpots->aErreurs);
// recovery of limit data, coeffr, coeffn
list($limites,$coeffr,$coeffn)=$oImpots->getData();
// we disconnect
$oImpots->disconnect();
// we return the result
if(count($oImpots->aErreurs)!=0) return array($oImpots->aErreurs);
else return array(array(),$limites,$coeffr,$coeffn);
}//getData
يقوم البرنامج النصي بما يفترض أن يقوم به: حساب الضريبة. سنترك للقارئ مهمة فك رموز كود المعالجة. ما يهمنا هو الحالات التي قد تنشأ نتيجة لهذا الإجراء:
- البيانات المدخلة غير صحيحة أو هناك مشكلة في الوصول إلى البيانات: يتم تعيين التطبيق إلى الحالة [e-errors]. يوضح ملف التكوين أن البرنامج النصي e-errors.php سيكون مسؤولاً عن إنشاء عرض الاستجابة:
<?php
…
$dConfig['etats']['e-erreurs']=array(
'actionsautorisees'=>array('get:retourformulaire','get:init'),
'vue'=>'e-erreurs.php');
- في جميع الحالات الأخرى، يتم تعيين التطبيق على الحالة [e-form] مع تحديد نمط حساب الضريبة في $dSession['state']['secondary']. يوضح ملف التكوين أن البرنامج النصي e-formulaire.php سيقوم بإنشاء عرض الاستجابة. وسيستخدم قيمة $dSession['state']['secondary'] لإنشاء نموذج مملوء مسبقًا بالقيم التي أدخلها المستخدم ومبلغ الضريبة.
4.12.4. الإجراء post:effacerformulaire
يتم ربطها من خلال التكوين بالنص البرمجي a-init.php الذي تم وصفه سابقًا.
4.12.5. إجراء get:return-form
تسمح لك بالعودة إلى حالة [e-form] من حالة [e-errors]. يتولى البرنامج النصي a-retourformulaire.php معالجة هذا الإجراء:
البرنامج النصي a-retourformulaire.php هو كما يلي:
<?php
// the input form is displayed
$dSession['etat']=array('principal'=>'e-formulaire','secondaire'=>'retourformulaire');
?>
نطلب ببساطة أن يتم تعيين التطبيق على الحالة [e-form] في متغير [form-return]. يوضح لنا ملف التكوين أن وحدة التحكم ستقوم بتنفيذ البرنامج النصي e-form.php لإنشاء الاستجابة للعميل.
<?php
…
$dConfig['etats']['e-formulaire']=array(
'actionsautorisees'=>array('post:calculerimpot','get:init','post:effacerformulaire'),
'vue'=>'e-formulaire.php');
سيقوم البرنامج النصي e-formulaire.php بإنشاء عرض [v-formulaire] في متغيره [retourformulaire]، أي النموذج المملوء مسبقًا بالقيم التي أدخلها المستخدم ولكن بدون مبلغ الضريبة.
4.13. تسلسل إجراءات غير صالح
يتم تعيين الإجراءات الصالحة من حالة معينة للتطبيق عن طريق التكوين:
<?php
…
// application status configuration
$dConfig['etats']['e-formulaire']=array(
'actionsautorisees'=>array('post:calculerimpot','get:init','post:effacerformulaire'),
'vue'=>'e-formulaire.php');
$dConfig['etats']['e-erreurs']=array(
'actionsautorisees'=>array('get:retourformulaire','get:init'),
'vue'=>'e-erreurs.php');
$dConfig['etats']['sansetat']=array('actionsautorisees'=>array('get:init'));
لقد سبق أن شرحنا هذا التكوين. إذا تم الكشف عن تسلسل غير صالح من الإجراءات، يتم تنفيذ البرنامج النصي a-invalid-sequence.php:
فيما يلي كود هذا البرنامج النصي:
<?php
// invalid sequence of actions
$dReponse['erreurs']=array("Enchaînement d'actions invalide");
$dSession['etat']=array('principal'=>'e-erreurs','secondaire'=>'enchainementinvalide');
?>
يؤدي هذا إلى تعيين التطبيق إلى الحالة [e-errors]. نقدم معلومات في $dSession['state']['secondary'] سيتم استخدامها بواسطة منشئ صفحة الخطأ. كما رأينا سابقًا، هذا المنشئ هو e-errors.php:
<?php
…
$dConfig['etats']['e-erreurs']=array(
'actionsautorisees'=>array('get:retourformulaire','get:init'),
'vue'=>'e-erreurs.php');
سنلقي نظرة على كود هذا المولد لاحقًا. العرض المرسَل إلى العميل هو كما يلي:

4.14. طرق عرض التطبيق
4.14.1. عرض العرض النهائي
دعونا نلقي نظرة على كيفية إرسال وحدة التحكم للاستجابة إلى العميل بمجرد تنفيذ الإجراء الذي طلبه العميل:
<?php
…
....
// start or resume session
session_start();
$dSession=$_SESSION["session"];
if($dSession) $dSession=unserialize($dSession);
// retrieve the action to be taken
$sAction=$_GET['action'] ? strtolower($_GET['action']) : 'init';
$sAction=strtolower($_SERVER['REQUEST_METHOD']).":$sAction";
// is the sequence of actions normal?
if( ! enchainementOK($dConfig,$dSession,$sAction)){
// abnormal sequence
$sAction='enchainementinvalide';
}//if
// share processing
$scriptAction=$dConfig['actions'][$sAction] ?
$dConfig['actions'][$sAction]['url'] :
$dConfig['actions']['actionInvalide']['url'];
include $scriptAction;
// send response(view) to customer
$sEtat=$dSession['etat']['principal'];
$scriptVue=$dConfig['etats'][$sEtat]['vue'];
include $scriptVue;
.....
// ---------------------------------------------------------------
function finSession(&$dConfig,&$dReponse,&$dSession){
// $dConfig: configuration dictionary
// $dSession: dictionary containing session information
// $dReponse: the dictionary of arguments for the response page
// session registration
...
// reply sent to customer
include $dConfig['vuesReponse'][$dReponse['vuereponse']]['url'];
// end of script
exit(0);
}//endsession
عند العودة من نص برمجي للإجراء، يسترد وحدة التحكم الحالة التي يجب أن يضع فيها التطبيق من $dSession['etat']['principal']. تم تعيين هذه الحالة بواسطة الإجراء الذي تم تنفيذه للتو. ثم تقوم وحدة التحكم بتنفيذ مولد العرض المرتبط بتلك الحالة. وتجد اسم مولد العرض في ملف التكوين. دور مولد العرض هو كما يلي:
- تعيين اسم قالب الاستجابة الذي سيتم استخدامه في $dResponse['responseView']. سيتم تمرير هذه المعلومات إلى وحدة التحكم. القالب هو تكوين من طرق العرض الأساسية التي تشكل، عند دمجها، طريقة العرض النهائية.
- يُعد المعلومات الديناميكية التي سيتم عرضها في العرض النهائي. هذه الخطوة مستقلة عن وحدة التحكم. وهي بمثابة واجهة بين مُنشئ العرض والعرض النهائي. وهي خاصة بكل تطبيق.
- يجب أن تنتهي باستدعاء وظيفة finSession الخاصة بوحدة التحكم. ستقوم هذه الوظيفة
- تحفظ الجلسة
- إرسال الاستجابة
فيما يلي كود دالة finSession:
<?php
…
// ---------------------------------------------------------------
function finSession(&$dConfig,&$dReponse,&$dSession){
// $dConfig: configuration dictionary
// $dSession: dictionary containing session information
// $dReponse: the dictionary of arguments for the response page
// session registration
...
// reply sent to customer
include $dConfig['vuesReponse'][$dReponse['vuereponse']]['url'];
// end of script
exit(0);
}//endsession
يتم تحديد العرض المرسَل إلى المستخدم بواسطة الكيان $dResponse['responseView']، الذي يحدد القالب الذي سيُستخدم للاستجابة النهائية.
4.14.2. قالب الاستجابة
سيقوم التطبيق بإنشاء استجاباته المختلفة بناءً على القالب الفردي التالي:
![]() |
يرتبط هذا النموذج بالمفتاح 'modele1' في قاموس $dConfig['vuesreponse']:
البرنامج النصي m-reponse.php مسؤول عن إنشاء هذا القالب:
<html>
<head>
<title>Application impots</title>
<link type="text/css" href="<?php echo $dReponse['urlstyle'] ?>" rel="stylesheet" />
</head>
<body>
<?php
include $dReponse['vue1'];
?>
<hr>
<?php
include $dReponse['vue2'];
?>
</body>
</html>
يحتوي هذا البرنامج النصي على ثلاثة عناصر ديناميكية مخزنة في قاموس $dReponse ومرتبطة بالمفاتيح التالية:
- urlstyle: عنوان URL لورقة أنماط القالب
- view1: اسم البرنامج النصي المسؤول عن إنشاء عرض view1
- vue2: اسم البرنامج النصي المسؤول عن إنشاء عرض vue2
يجب على مولد العرض الذي يرغب في استخدام قالب modèle1 تعريف هذه العناصر الديناميكية الثلاثة. نحدد الآن العروض الأساسية التي يمكن أن تحل محل العناصر [vue1] و [vue2] في القالب.
4.14.3. طريقة العرض الأساسية v-bandeau.php
يُنشئ البرنامج النصي v-bandeau.php عرضًا سيتم وضعه في منطقة [vue1]:
<table>
<tr>
<td><img src="univ01.gif"></td>
<td>
<table>
<tr>
<td><div class='titre'><?php echo $dReponse['titre'] ?></div></td>
</tr>
<tr>
<td><div class='resultat'><?php echo $dReponse['resultat']?></div></td>
</tr>
</table>
</td>
</tr>
</table>
يجب أن يحدد مولد العرض عنصرين ديناميكيين موضوعين في قاموس $dReponse ومرتبطين بالمفاتيح التالية:
title: title to display
result: amount of tax due
4.14.4. العرض الأساسي v-form.php
يتوافق القسم [vue2] مع عرض [v-form] أو عرض [v-errors]. يتم إنشاء عرض [v-form] بواسطة البرنامج النصي v-formulaire.php:
<form method="post" action="main.php?action=calculerimpot">
<table>
<tr>
<td class="libelle">Etes-vous marié(e)</td>
<td class="valeur">
<input type="radio" name="optmarie" <?php echo $dReponse['optoui'] ?> value="oui">oui
<input type="radio" name="optmarie" <?php echo $dReponse['optnon'] ?> value="non">non
<td>
<tr>
<td class="libelle">Nombre d'enfants</td>
<td class="valeur">
<input type="text" class="text" name="txtenfants" size="3" value="<?php echo $dReponse['enfants'] ?>"
</td>
</tr>
<tr>
<td class="libelle">Salaire annuel</td>
<td class="valeur">
<input type="text" class="text" name="txtsalaire" size="10" value="<?php echo $dReponse['salaire'] ?>"
</td>
</tr>
</table>
<hr>
<input type="submit" class="submit" value="Calculer l'impôt">
</form>
<form method="post" action="main.php?action=effacerformulaire">
<input type="submit" class="submit" value="Effacer le formulaire">
</form>
الأجزاء الديناميكية من هذا العرض التي يجب تعريفها بواسطة مولد العرض مرتبطة بالمفاتيح التالية في قاموس $dReponse:
- optoui: حالة زر الاختيار المسمى optoui
- optnon: حالة زر الاختيار المسمى optnon
- children: عدد الأطفال الذين سيتم وضعهم في حقل txtenfants
- salary: الراتب السنوي الذي سيتم وضعه في حقل txtsalary
4.14.5. العرض الأساسي v-erreurs.php
يتم إنشاء عرض [v-errors] بواسطة البرنامج النصي v-errors.php:
Les erreurs suivantes se sont produites :
<ul>
<?php
for($i=0;$i<count($dReponse["erreurs"]);$i++){
echo "<li class='erreur'>".$dReponse["erreurs"][$i]."</li>\n";
}//for
?>
</ul>
<div class="info"><?php echo $dReponse["info"] ?></div>
<br>
<a href="<?php echo $dReponse["href"] ?>"><?php echo $dReponse["lien"] ?></a>
الأجزاء الديناميكية من هذا العرض التي سيتم تعريفها بواسطة مولد العرض مرتبطة بالمفاتيح التالية في قاموس $dReponse:
- errors: مجموعة من رسائل الخطأ
- info: رسالة معلومات
- link: نص الرابط
- href: عنوان URL الهدف للرابط أعلاه
4.14.6. ورقة الأنماط
يتم تصميم جميع طرق العرض بواسطة ورقة أنماط. لتغيير المظهر المرئي للتطبيق، يمكنك تعديل ورقة الأنماط الخاصة به. ورقة الأنماط style1.css التالية:
div.menu {
background-color: #FFD700;
color: #F08080;
font-weight: bolder;
text-align: center;
}
td.separateur {
background: #FFDAB9;
width: 20px;
}
table.modele2 {
width: 600px;
}
BODY {
background-image : url(standard.jpg);
margin-left : 0px;
margin-top : 6px;
color : #4A1919;
font-size: 10pt;
font-family: Arial, Helvetica, sans-serif;
scrollbar-face-color:#F2BE7A;
scrollbar-arrow-color:#4A1919;
scrollbar-track-color:#FFF1CC;
scrollbar-3dlight-color:#CBB673;
scrollbar-darkshadow-color:#CBB673;
}
div.titre {
font: 30pt Garamond;
color: #FF8C00;
background-color: Yellow;
}
table.menu {
background-color: #ADD8E6;
}
A:HOVER {
text-decoration: underline;
color: #FF0000;
background-color : transparent;
}
A:ACTIVE {
text-decoration: underline;
color : #BF4141;
background-color : transparent;
}
A:VISITED {
color : #BF4141;
background-color : transparent;
}
.error {
color : red;
font-weight : bold;
}
INPUT.text {
margin-left : 3px;
font-size:8pt;
font-weight:bold;
color:#4A1919;
background-color:#FFF6E0;
border-right:1px solid;
border-left:1px solid;
border-top:1px solid;
border-bottom:1px solid;
}
td.libelle {
background-color: #F0FFFF;
color: #0000CD;
}
td.valeur {
background-color: #DDA0DD;
}
DIV.resultat {
background-color : #FFA07A;
font : bold 12pt;
}
div.info {
color: #FA8072;
}
li.erreur {
color: #DC143C;
}
INPUT.submit {
margin-left : 6px;
font-size:8pt;
font-weight:bold;
color:#4A1919;
background-color:#FFF1CC;
border-right:1px solid;
border-left:1px solid;
border-top:1px solid;
border-bottom:1px solid;
}
4.15. عرض المولدات
4.15.1. دور مُنشئ العرض
دعونا نراجع مقتطف كود وحدة التحكم الذي ينفذ مولد العرض:
<?php
…
// share processing
$scriptAction=$dConfig['actions'][$sAction] ?
$dConfig['actions'][$sAction]['url'] :
$dConfig['actions']['actionInvalide']['url'];
include $scriptAction;
// send response(view) to customer
$sEtat=$dSession['etat']['principal'];
$scriptVue=$dConfig['etats'][$sEtat]['vue'];
include $scriptVue;
يتم ربط مولد العرض بالحالة التي سيتم وضع التطبيق فيها. يتم تعيين الارتباط بين الحالة ومولد العرض عبر التكوين:
<?php
…
// application status configuration
$dConfig['etats']['e-formulaire']=array(
'actionsautorisees'=>array('post:calculerimpot','get:init','post:effacerformulaire'),
'vue'=>'e-formulaire.php');
$dConfig['etats']['e-erreurs']=array(
'actionsautorisees'=>array('get:retourformulaire','get:init'),
'vue'=>'e-erreurs.php');
$dConfig['etats']['sansetat']=array('actionsautorisees'=>array('get:init'));
لقد سبق أن شرحنا دور مولد العرض. دعونا نراجعه هنا. مولد العرض:
- يحدد اسم قالب الاستجابة الذي سيتم استخدامه في $dResponse['responseView']. سيتم تمرير هذه المعلومات إلى وحدة التحكم. القالب هو تكوين من طرق العرض الأساسية التي تشكل، عند دمجها، طريقة العرض النهائية.
- يقوم بإعداد المعلومات الديناميكية التي سيتم عرضها في العرض النهائي. هذه الخطوة مستقلة عن وحدة التحكم. وهي بمثابة واجهة بين مولد العرض والعرض النهائي. وهي خاصة بكل تطبيق.
- يجب أن تنتهي باستدعاء وظيفة finSession الخاصة بوحدة التحكم. ستقوم هذه الوظيفة
- تحفظ الجلسة
- إرسال الاستجابة
4.15.2. مولد العرض المرتبط بحالة [e-form]
يُسمى البرنامج النصي المسؤول عن إنشاء العرض المرتبط بحالة [e-form] e-form.php:
<?php
…
$dConfig['etats']['e-formulaire']=array(
'actionsautorisees'=>array('post:calculerimpot','get:init','post:effacerformulaire'),
'vue'=>'e-formulaire.php');
وإليك الكود:
<?php
// we prepare the answer form
$dReponse['titre']=$dConfig['webapp']['titre'];
$dReponse['vuereponse']='modele1';
$dReponse['vue1']=$dConfig['vues']['bandeau']['url'];
$dReponse['vue2']=$dConfig['vues']['formulaire']['url'];
$dReponse['urlstyle']=$dConfig['style']['url'];
$dReponse['titre']=$dConfig['webapp']['titre'];
// configuration according to the type of form to be generated
$type=$dSession['etat']['secondaire'];
if($type=='init'){
// empty form
$dReponse['optnon']='checked';
}//if
if($type=='calculimpot'){
// we need to redisplay the input parameters stored in the query
$dReponse['optoui']=$_POST['optmarie']=='oui' ? 'checked' : '';
$dReponse['optnon']=$dReponse['optoui'] ? '' : 'checked';
$dReponse['enfants']=$_POST['txtenfants'];
$dReponse['salaire']=$_POST['txtsalaire'];
$dReponse['resultat']='Impôt à payer : '.$dReponse['impot'].' F';
}//if
if($type=='retourformulaire'){
// we need to redisplay the input parameters stored in the session
$dReponse['optoui']=$dSession['requete']['optmarie']=='oui' ? 'checked' : '';
$dReponse['optnon']=$dReponse['optoui']=='' ? 'checked' : '';
$dReponse['enfants']=$dSession['requete']['txtenfants'];
$dReponse['salaire']=$dSession['requete']['txtsalaire'];
}//if
// we send the answer
finSession($dConfig,$dReponse,$dSession);
?>
لاحظ أن مولد العرض يتوافق مع متطلبات مولد العرض:
- تعيين قالب الاستجابة المراد استخدامه في $dReponse['vuereponse']
- تمرير المعلومات إلى هذا القالب. هنا، يتم تمريرها عبر قاموس $dResponse.
- الانتهاء باستدعاء وظيفة finSession الخاصة بوحدة التحكم
هنا، القالب المستخدم هو 'template1'. لذلك، يحدد مولد العرض معلومتين مطلوبتين لهذا القالب: $dResponse['view1'] و $dResponse['view2'].
في الحالة المحددة لتطبيقنا، تعتمد طريقة العرض المرتبطة بحالة [e-form] على المعلومات المخزنة في المتغير $dSession['etat']['secondaire']. هذا اختيار تصميمي. قد يختار تطبيق آخر تمرير معلومات إضافية بطريقة مختلفة. علاوة على ذلك، يتم هنا وضع جميع المعلومات اللازمة لعرض طريقة العرض النهائية في قاموس $dReponse. مرة أخرى، هذا خيار متروك للمطور. يمكن أن تحدث حالة [e-form] بعد أربعة إجراءات مختلفة: init، calculateTax، returnForm، clearForm. العرض المراد عرضه ليس هو نفسه تمامًا في جميع الحالات. لذلك، قمنا بتمييز ثلاث حالات هنا في $dSession['state']['secondary']:
- init: يتم عرض النموذج فارغًا
- calculateTax: يتم عرض النموذج مع مبلغ الضريبة والبيانات المستخدمة لحسابها
- returnform: يتم عرض النموذج مع البيانات التي تم إدخالها في البداية
أعلاه، يستخدم البرنامج النصي e-formulaire.php هذه المعلومات لتقديم الاستجابة وفقًا لهذه المتغيرات الثلاثة.
4.15.3. طريقة العرض المرتبطة بحالة [e-errors]
يُسمى البرنامج النصي المسؤول عن إنشاء العرض المرتبط بحالة [e-errors] e-errors.php وهو كما يلي:
<?php
// we prepare the answer errors
$dReponse['titre']=$dConfig['webapp']['titre'];
$dReponse['vuereponse']='modele1';
$dReponse['vue1']=$dConfig['vues']['bandeau']['url'];
$dReponse['vue2']=$dConfig['vues']['erreurs']['url'];
$dReponse['urlstyle']=$dConfig['style']['url'];
$dReponse['titre']=$dConfig['webapp']['titre'];
$dReponse['lien']='Retour au formulaire de saisie';
$dReponse['href']='main.php?action=retourformulaire';
// additional information
$type=$dSession['etat']['secondaire'];
if($type=='database'){
$dReponse['info']="Veuillez avertir l'administrateur de l'application";
}
// we send the answer
finSession($dConfig,$dReponse,$dSession);
?>
4.15.4. عرض العرض النهائي
ينتهي كلا البرنامجين النصيين اللذين يولدان العرضين النهائيين باستدعاء وظيفة finSession الخاصة بوحدة التحكم:
<?php
…
function finSession(&$dConfig,&$dReponse,&$dSession){
// $dConfig: configuration dictionary
// $dSession: dictionary containing session information
// $dReponse: the dictionary of arguments for the response page
....
// we present the answer
include $dConfig['vuesReponse'][$dReponse['vuereponse']]['url'];
// end of script
exit(0);
}//endsession
يتم تحديد العرض المرسَل إلى المستخدم بواسطة الكيان $dResponse['responseView']، الذي يحدد القالب الذي سيُستخدم للاستجابة النهائية. بالنسبة لحالتَي [e-form] و[e-errors]، تم تعيين هذا القالب على template1:
يتوافق هذا القالب مع البرنامج النصي m-reponse.php:
4.16. تعديل قالب الاستجابة
هنا، نفترض أننا قررنا تغيير المظهر المرئي للاستجابة المرسلة إلى العميل، ونحن مهتمون بفهم الآثار المترتبة على ذلك في الكود.
4.16.1. القالب الجديد
ستكون بنية الاستجابة الآن كما يلي:
![]() |
سيُطلق على هذا النموذج اسم template2، وسيُطلق على البرنامج النصي المسؤول عن إنشاء هذا النموذج اسم m-response2.php:
النص البرمجي المطابق لهذا القالب هو كما يلي:
<html>
<head>
<title>Application impots</title>
<link type="text/css" href="<?php echo $dReponse['urlstyle'] ?>" rel="stylesheet" />
</head>
<body>
<table class='modele2'>
<!-- start banner -->
<tr>
<td colspan="2"><?php include $dReponse['vue1']; ?></td>
</tr>
<!-- slim band -->
<tr>
<!-- start menu -->
<td><?php include $dReponse['vue2']; ?></td>
<!-- end menu -->
<!-- start zone 3 -->
<td><?php include $dReponse['vue3']; ?></td>
<!-- end of zone 3 -->
</tr>
</table>
</body>
</html>
العناصر الديناميكية للقالب هي كما يلي:
- $dReponse['urlstyle']: ورقة الأنماط المطلوب استخدامها
- $dReponse['vue1']: البرنامج النصي الذي سيتم استخدامه لإنشاء [vue1]
- $dReponse['vue2']: البرنامج النصي الذي سيتم استخدامه لإنشاء [vue2]
- $dReponse['vue3']: البرنامج النصي الذي سيتم استخدامه لإنشاء [vue3]
يجب تعيين هذه العناصر بواسطة مولدات العرض.
4.16.2. صفحات الاستجابة المختلفة
سيقدم التطبيق الآن الاستجابات التالية للمستخدم. عند الاستدعاء الأولي، ستكون صفحة الاستجابة كما يلي:
![]() |
إذا قدم المستخدم بيانات صحيحة، يتم حساب الضريبة:
![]() |
إذا ارتكب أخطاء في الإدخال، يتم عرض صفحة الخطأ:
![]() |
إذا استخدموا رابط [العودة إلى نموذج الإدخال]، فسوف يرون النموذج كما تم إرساله آخر مرة:
![]() |
إذا استخدموا، في السيناريو أعلاه، رابط [إعادة تعيين النموذج]، فسوف يرون نموذجًا فارغًا:
![]() |
لاحظ أن التطبيق يستخدم نفس الإجراءات كما في السابق. لم يتغير سوى شكل الردود.
4.16.3. طرق العرض الأساسية
سيتم ربط العرض الأساسي [view1] بالبرنامج النصي v-bandeau.php، كما في المثال السابق:
<table>
<tr>
<td><img src="univ01.gif"></td>
<td>
<table>
<tr>
<td><div class='titre'><?php echo $dReponse['titre'] ?></div></td>
</tr>
<tr>
<td><div class='resultat'><?php echo $dReponse['resultat']?></div></td>
</tr>
</table>
</td>
</tr>
</table>
تحتوي هذه الطريقة على عنصرين ديناميكيين:
- $dResponse['title']: العنوان المراد عرضه
- $dReponse['resultat']: مبلغ الضريبة المستحقة
سيتم ربط العرض الأساسي [vue2] بالنص البرمجي v-menu.php التالي:
<table class="menu">
<tr>
<td><div class="menu">Options</div></td>
</tr>
<?php
for($i=0;$i<count($dReponse['liens']);$i++){
echo '<tr><td><div class="option"><a href="'.
$dReponse['liens'][$i]['url'].
'">'.$dReponse['liens'][$i]['texte']."</a></div></td></tr>\n";
}//$i
?>
</table>
تحتوي هذه العرض على العناصر الديناميكية التالية:
- $dResponse['links']: مصفوفة من الروابط لعرضها في [view2]. كل عنصر في المصفوفة عبارة عن قاموس يحتوي على مفتاحين:
- 'url': عنوان URL الهدف للرابط
- 'text': نص الرابط
سيتم ربط العرض الأساسي [view3] بالنص البرمجي v-form2.php إذا كنت ترغب في عرض نموذج الإدخال، أو بالنص البرمجي v-errors2.php إذا كنت ترغب في عرض صفحة الخطأ. فيما يلي كود النص البرمجي v-form2.php:
<form method="post" action="main.php?action=calculerimpot">
<table>
<tr>
<td class="libelle">Etes-vous marié(e)</td>
<td class="valeur">
<input type="radio" name="optmarie" <?php echo $dReponse['optoui'] ?> value="oui">oui
<input type="radio" name="optmarie" <?php echo $dReponse['optnon'] ?> value="non">non
</td>
</tr>
<tr>
<td class="libelle">Nombre d'enfants</td>
<td class="valeur">
<input type="text" class="text" name="txtenfants" size="3" value="<?php echo $dReponse['enfants'] ?>"
</td>
</tr>
<tr>
<td class="libelle">Salaire annuel</td>
<td class="valeur">
<input type="text" class="text" name="txtsalaire" size="10" value="<?php echo $dReponse['salaire'] ?>"
</td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" class="submit" value="Calculer l'impôt"></td>
</tr>
</table>
</form>
ترتبط الأجزاء الديناميكية في هذه الواجهة، والتي يجب أن يحددها مُنشئ الواجهة، بالمفاتيح التالية في قاموس $dResponse:
- optoui: حالة زر الاختيار المسمى optoui
- optnon: حالة زر الاختيار المسمى optnon
- children: عدد الأطفال الذين سيتم وضعهم في حقل txtenfants
- salaire: الراتب السنوي الذي سيتم وضعه في حقل txtsalaire
يُسمى البرنامج النصي الذي يُنشئ صفحة الخطأ v-erreurs2.php. وفيما يلي كوده:
Les erreurs suivantes se sont produites :
<ul>
<?php
for($i=0;$i<count($dReponse["erreurs"]);$i++){
echo "<li class='erreur'>".$dReponse["erreurs"][$i]."</li>\n";
}//for
?>
</ul>
<div class="info"><?php echo $dReponse["info"] ?></div>
ترتبط الأجزاء الديناميكية من هذا العرض التي سيتم تعريفها بواسطة مولد العرض بالمفاتيح التالية في قاموس $dReponse:
errors: array of error messages
info: information message
4.16.4. ورقة الأنماط
لم تتغير. لا تزال هي style1.css.
4.16.5. ملف التكوين الجديد
لتنفيذ هذه العروض الجديدة، نحتاج إلى تعديل بعض الأسطر في ملف التكوين.
<?php
// php configuration
ini_set("register_globals","off");
ini_set("display_errors","off");
ini_set("expose_php","off");
// list of modules to be included
$dConfig['includes']=array('c-impots-data.php','c-impots-calcul.php');
// application controller
$dConfig['webapp']=array('titre'=>"Calculez votre impôt");
// aplication view configuration
$dConfig['vuesReponse']['modele1']=array('url'=>'m-reponse.php');
$dConfig['vuesReponse']['modele2']=array('url'=>'m-reponse2.php');
$dConfig['vues']['formulaire']=array('url'=>'v-formulaire.php');
$dConfig['vues']['erreurs']=array('url'=>'v-erreurs.php');
$dConfig['vues']['formulaire2']=array('url'=>'v-formulaire2.php');
$dConfig['vues']['erreurs2']=array('url'=>'v-erreurs2.php');
$dConfig['vues']['bandeau']=array('url'=>'v-bandeau.php');
$dConfig['vues']['menu']=array('url'=>'v-menu.php');
$dConfig['style']['url']='style1.css';
// configuration of application actions
$dConfig['actions']['get:init']=array('url'=>'a-init.php');
$dConfig['actions']['post:calculerimpot']=array('url'=>'a-calculimpot.php');
$dConfig['actions']['get:retourformulaire']=array('url'=>'a-retourformulaire.php');
$dConfig['actions']['post:effacerformulaire']=array('url'=>'a-init.php');
$dConfig['actions']['enchainementinvalide']=array('url'=>'a-enchainementinvalide.php');
$dConfig['actions']['actionInvalide']=array('url'=>'a-actioninvalide.php');
// application status configuration
$dConfig['etats']['e-formulaire']=array(
'actionsautorisees'=>array('post:calculerimpot','get:init','post:effacerformulaire'),
'vue'=>'e-formulaire2.php');
$dConfig['etats']['e-erreurs']=array(
'actionsautorisees'=>array('get:retourformulaire','get:init'),
'vue'=>'e-erreurs2.php');
$dConfig['etats']['sansetat']=array('actionsautorisees'=>array('get:init'));
// model application configuration
$dConfig["DSN"]=array(
"sgbd"=>"mysql",
"user"=>"seldbimpots",
"mdp"=>"mdpseldbimpots",
"host"=>"localhost",
"database"=>"dbimpots"
);
?>
يتضمن التغيير الرئيسي تحديث مولدات العرض المرتبطة بتقارير [e-form] و [e-errors]. وبمجرد الانتهاء من ذلك، تتولى مولدات العرض الجديدة مسؤولية إنشاء صفحات الاستجابة الجديدة.
4.16.6. مولد العرض المرتبط بتقرير [e-form]
في ملف التكوين، أصبحت حالة [e-form] مرتبطة الآن بمولد العرض e-form2.php. وفيما يلي كود هذا البرنامج النصي:
<?php
// we prepare the answer form
$dReponse['titre']=$dConfig['webapp']['titre'];
$dReponse['vuereponse']='modele2';
$dReponse['vue1']=$dConfig['vues']['bandeau']['url'];
$dReponse['vue2']=$dConfig['vues']['menu']['url'];
$dReponse['vue3']=$dConfig['vues']['formulaire2']['url'];
$dReponse['urlstyle']=$dConfig['style']['url'];
$dReponse['titre']=$dConfig['webapp']['titre'];
$dReponse['liens']=array(
array('texte'=>'Réinitialiser le formulaire', 'url'=>'main.php?action=init')
);
// configuration according to the type of form to be generated
$type=$dSession['etat']['secondaire'];
if($type=='init'){
// empty form
$dReponse['optnon']='checked';
}//if
if($type=='calculimpot'){
// we need to redisplay the input parameters stored in the query
$dReponse['optoui']=$_POST['optmarie']=='oui' ? 'checked' : '';
$dReponse['optnon']=$dReponse['optoui'] ? '' : 'checked';
$dReponse['enfants']=$_POST['txtenfants'];
$dReponse['salaire']=$_POST['txtsalaire'];
$dReponse['resultat']='Impôt à payer : '.$dReponse['impot'].' F';
}//if
if($type=='retourformulaire'){
// we need to redisplay the input parameters stored in the session
$dReponse['optoui']=$dSession['requete']['optmarie']=='oui' ? 'checked' : '';
$dReponse['optnon']=$dReponse['optoui']=='' ? 'checked' : '';
$dReponse['enfants']=$dSession['requete']['txtenfants'];
$dReponse['salaire']=$dSession['requete']['txtsalaire'];
}//if
// we send the answer
finSession($dConfig,$dReponse,$dSession);
?>
التغييرات الرئيسية هي كما يلي:
- يشير مولد العرض إلى أنه يريد استخدام قالب الاستجابة modele2
- لهذا السبب، يقوم بتعبئة العناصر الديناميكية $dReponse['vue1'], $dReponse['vue2'], $dReponse['vue3'], وجميعها مطلوبة من قبل قالب الاستجابة modele2.
- يقوم مولد العرض أيضًا بتعبئة العنصر الديناميكي $dResponse['links'], الذي يحدد الروابط التي سيتم عرضها في منطقة [view2] من الاستجابة.
4.16.7. مولد العرض المرتبط بحالة [e-errors]
في ملف التكوين، أصبحت حالة [e-errors] مرتبطة الآن بمولد العرض e-errors2.php. فيما يلي كود هذا البرنامج النصي:
<?php
// we prepare the answer errors
$dReponse['titre']=$dConfig['webapp']['titre'];
$dReponse['vuereponse']='modele2';
$dReponse['vue1']=$dConfig['vues']['bandeau']['url'];
$dReponse['vue2']=$dConfig['vues']['menu']['url'];
$dReponse['vue3']=$dConfig['vues']['erreurs2']['url'];
$dReponse['urlstyle']=$dConfig['style']['url'];
$dReponse['titre']=$dConfig['webapp']['titre'];
$dReponse['liens']=array(
array('texte'=>'Retour au formulaire de saisie', 'url'=>'main.php?action=retourformulaire')
);
// additional information
$type=$dSession['etat']['secondaire'];
if($type=='database'){
$dReponse['info']="Veuillez avertir l'administrateur de l'application";
}
// we send the answer
finSession($dConfig,$dReponse,$dSession);
?>
التغييرات التي تم إجراؤها مطابقة لتلك التي تم إجراؤها على مولد العرض e-formulaire2.php.
4.17. الخلاصة
تمكنا من إثبات فوائد وحدة التحكم العامة لدينا باستخدام مثال. لم نضطر إلى كتابتها بأنفسنا. لقد قمنا ببساطة بكتابة نصوص الإجراءات ومولدات العرض وطرق العرض للتطبيق. كما أظهرنا فوائد فصل الإجراءات عن طرق العرض. وقد سمح لنا ذلك بتغيير مظهر الاستجابات دون تعديل سطر واحد من التعليمات البرمجية في نصوص الإجراءات. تم تعديل النصوص المتعلقة بتوليد العرض فقط. ولكي يكون ذلك ممكنًا، يجب ألا تفترض نصوص الإجراءات أي شيء بشأن العرض الذي سيعرض المعلومات التي قامت بحسابها. يجب أن تعيد هذه المعلومات ببساطة إلى وحدة التحكم، التي تمررها إلى مولد العرض لتنسيقها. هذه قاعدة مطلقة: يجب فصل الإجراء تمامًا عن العروض.
في هذا الفصل، استكشفنا فلسفة Struts، المعروفة جيدًا لمطوري Java. يتيح مشروع مفتوح المصدر يسمى php.mvc تطوير الويب/PHP باستخدام فلسفة Struts. قم بزيارة http://www.phpmvc.net/ لمزيد من المعلومات.













