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.2 3309.5。 此时税额 I 等于 0.2*R - 3309.5*nbParts。如果 QF 使得条件 QF <= limit 永远无法满足,则使用最后一行中的系数:0 0.65 49062,由此得出的税额 I 为 0.65*R - 49062*nbParts。 |
4.2. 数据库
上述数据存储在一个名为 dbimpots 的 MySQL 数据库中。用户 seldbimpots(密码为 mdpseldbimpots)对数据库内容具有只读权限。该数据库包含一个名为 impots 的表,其结构如下:

其内容如下:

4.3. 应用程序的MVC架构
该应用程序将采用以下MVC架构:
![]() |
- main.php 控制器即为上述所述的通用控制器
- 客户端的请求以 main.php?action=xx 形式的查询发送至控制器。action 参数的值决定了应执行 ACTIONS 块中的哪个脚本。被执行的操作脚本会向控制器返回一个变量,该变量指示 Web 应用程序应处于何种状态。基于此状态,控制器将调用其中一个视图生成器,向客户端发送响应。
- impots-data.php 是负责向控制器提供所需数据的类
- impots-calcul.php 是负责计算税款的业务逻辑类
4.4. 数据访问类
数据访问类旨在将数据源从 Web 应用程序中隐藏起来。其接口包含一个 getData 方法,该方法返回计算税额所需的三个数据数组。在本示例中,数据是从 MySQL 数据库中检索的。为了使该类与实际的数据库管理系统 (DBMS) 类型无关,我们将使用附录中描述的 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. Web 应用程序操作
共有四个操作:
- get:init: 这是在向控制器发送初始请求(不带参数)时触发的操作。它会渲染空的 'form' 视图。
- post:clearform:由 [清除表单] 按钮触发的操作。它渲染空的 [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');
每个操作都与负责处理它的脚本相关联。每个操作都会将 Web 应用程序带入由 $dSession['state']['main'] 元素定义的状态。该状态旨在保存在会话中。此外,操作还会在 $dResponse 字典中存储显示与应用程序新状态相关联的视图所需的信息。
4.10. Web 应用程序状态
共有两种:
[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'));
在某个状态下,允许的操作对应于与该状态关联的视图中链接或 [submit] 按钮的目标 URL。此外,'get:init' 操作始终被允许。这允许用户从浏览器的地址栏中获取 main.php 的 URL 并重新加载它,无论应用程序处于何种状态。这是一种“手动”重置。'sansetat' 状态仅在应用程序启动时存在。
每个应用程序状态都关联有一个脚本,负责生成与该状态相关的视图:
- state [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. Web 应用程序的 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. Web 应用程序操作
4.12.1. 操作脚本的一般运行机制
- 控制器会根据从客户端接收的 action 参数调用动作脚本。
- 执行完成后,操作脚本必须告知控制器将应用程序置于何种状态。该状态必须指定在 $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-errors] 状态返回至 [e-form] 状态。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. 响应模板
应用程序将基于以下单一模板生成各种响应:
![]() |
该模板与 $dConfig['vuesreponse'] 字典中的 'modele1' 键相关联:
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>
该脚本包含三个动态元素,这些元素存储在字典 $dResponse 中,并与以下键相关联:
- 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>
视图生成器必须在字典 $dResponse 中定义两个动态元素,并将其与以下键关联:
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);
?>
请注意,该视图生成器符合视图生成器的要求:
- 在 $dResponse['vuereponse'] 中设置要使用的响应模板
- 将信息传递给该模板。此处是通过 $dResponse 字典进行传递的。
- 最后调用控制器的 finSession 函数
此处使用的模板为 'template1'。因此,视图生成器定义了该模板所需的两项信息:$dResponse['view1'] 和 $dResponse['view2']。
就本应用的具体情况而言,与 [e-form] 状态关联的视图取决于变量 $dSession['etat']['secondaire'] 中存储的信息。这是一种设计选择。其他应用可能会选择以不同的方式传递额外信息。此外,此处显示最终视图所需的所有信息都放置在 $dResponse 字典中。 同样,这取决于开发者的选择。[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-response.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']: 要显示的标题
- $dResponse['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
- 因此,它填充了动态元素 $dResponse['vue1'], $dResponse['vue2'], $dResponse['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. 结论
通过一个示例,我们成功展示了通用控制器带来的优势。我们无需亲自编写控制器,只需为应用程序编写操作脚本、视图生成器和视图即可。我们还展示了将操作与视图分离的好处。这使我们能够在不修改操作脚本中任何一行代码的情况下,改变响应的外观。仅需修改参与视图生成的脚本。 要实现这一点,操作脚本绝不能对将要显示其计算结果的视图做出任何假设。它只需将信息返回给控制器,由控制器将其传递给视图生成器进行格式化。这是一条绝对规则:操作必须与视图完全解耦。
在本章中,我们探讨了 Java 开发者熟知的 Struts 理念。一个名为 php.mvc 的开源项目支持基于 Struts 理念进行 Web/PHP 开发。请访问 http://www.phpmvc.net/ 获取更多信息。













