4. مثالها
4.1. برنامه مالیات: مقدمه
در اینجا برنامه IMPOTS را معرفی میکنیم که در ادامه در موارد متعددی از آن استفاده خواهد شد. این برنامه برای محاسبه بدهی مالیاتی یک مؤدی طراحی شده است. ما مورد سادهشدهای را در نظر میگیریم که مؤدی تنها حقوق خود را برای اظهار دارد:
- تعداد سهام کارمند را به صورت nbParts = nbEnfants/2 + 1 در صورتی که مجرد باشد، محاسبه میکنیم، nbEnfants/2 + 2 اگر متأهل باشند، که در آن nbEnfants تعداد فرزندان آنهاست.
- اگر او حداقل سه فرزند داشته باشد، مستحق دریافت نیمپرداخت اضافی است
- درآمد مشمول مالیات او به صورت R = 0.72 × S محاسبه میشود، که در آن S حقوق سالانه او است.
- ضریب خانوادگی او به صورت QF = R / nbParts محاسبه میشود
- مالیات I او محاسبه میشود. جدول زیر را در نظر بگیرید:
۱۲٬۶۲۰٫۰ | 0 | 0 |
۱۳٬۱۹۰ | 0.05 | ۶۳۱ |
۱۵٬۶۴۰ | 0.1 | ۱٬۲۹۰٫۵ |
۲۴٬۷۴۰ | 0.15 | ۲٬۰۷۲٫۵ |
۳۱٬۸۱۰ | 0.2 | ۳٬۳۰۹٫۵ |
۳۹٬۹۷۰ | 0.25 | ۴٬۹۰۰ |
۴۸٬۳۶۰ | 0.3 | ۶۸۹۸.۵ |
۵۵٬۷۹۰ | 0.35 | ۹٬۳۱۶٫۵ |
۹۲٬۹۷۰ | 0.4 | ۱۲٬۱۰۶ |
۱۲۷٬۸۶۰ | 0.45 | ۱۶٬۷۵۴٫۵ |
۱۵۱٬۲۵۰ | 0.50 | ۲۳٬۱۴۷٫۵ |
۱۷۲٬۰۴۰ | 0.55 | ۳۰٬۷۱۰ |
۱۹۵٬۰۰۰ | 0.60 | ۳۹٬۳۱۲ |
0 | 0.65 | ۴۹٬۰۶۲ |
هر سطر شامل ۳ فیلد است. برای محاسبه مالیات I، به دنبال اولین سطری میگردیم که QF ≤ field1 باشد. برای مثال، اگر QF = 23000 باشد، سطر را پیدا خواهیم کرد
مالیه I سپس برابر است با 0.15*R - 2072.5*nbParts. اگر QF به گونهای باشد که شرط QF <= field1 هرگز برقرار نشود، ضرایب مربوط به آخرین خط استفاده میشوند. در این حالت:
که مالیات I را به صورت 0.65 × R – 49062 × nbParts میدهد.
دادههای تعریفکنندهٔ سطوح مختلف مالیاتی در یک پایگاه دادهٔ ODBC-MySQL ذخیره شدهاند. MySQL نسخهای در مالکیت عمومی از SGBD است که میتوان از آن روی پلتفرمهای مختلف، از جمله ویندوز و لینوکس، استفاده کرد. با استفاده از SGBD، یک پایگاه داده به نام dbimpots ایجاد شده است که شامل یک جدول واحد به نام impots است. دسترسی به پایگاه داده توسط نام کاربری و رمز عبور کنترل میشود، در این مورد admimpots/mdpimpots. اسکرینشات زیر نشان میدهد چگونه از پایگاه داده dbimpots با MySQL استفاده کنید:
C:\Program Files\EasyPHP\mysql\bin>mysql -u admimpots -p
Enter password: *********
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 18 to server version: 3.23.49-max-nt
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> use dbimpots;
Database changed
mysql> show tables;
+--------------------+
| Tables_in_dbimpots |
+--------------------+
| impots |
+--------------------+
1 row in set (0.00 sec)
mysql> describe impots;
+---------+--------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------+------+-----+---------+-------+
| limites | double | YES | | NULL | |
| coeffR | double | YES | | NULL | |
| coeffN | double | YES | | NULL | |
+---------+--------+------+-----+---------+-------+
3 rows in set (0.02 sec)
mysql> select * from impots;
+---------+--------+---------+
| limites | coeffR | coeffN |
+---------+--------+---------+
| 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 |
+---------+--------+---------+
14 rows in set (0.00 sec)
mysql>quit
پایگاه داده dbimpots به منبع داده ODBC به شرح زیر تبدیل میشود:
- مدیر منبع داده ۳۲ بیتی ODBC راهاندازی میشود

- برای افزودن منبع داده جدید ODBC از دکمه [Add] استفاده کنید

- درایور MySQL را انتخاب کرده و آن را روی [Terminer] تنظیم کنید
![]() |
- راننده MySQL تعدادی جزئیات را درخواست میکند:
۱ | نام DSN به منبع داده ODBC اختصاص داده شود – این میتواند هر چیزی باشد |
۲ | ماشینی که SGBD MySQL روی آن در حال اجرا است – در این مورد، localhost. شایان ذکر است که پایگاه داده میتواند یک پایگاه دادهٔ راه دور باشد. برنامههای محلی که از منبع داده ODBC استفاده میکنند، از این موضوع مطلع نخواهند بود. این امر بهویژه برای برنامه PHP ما صادق است. |
۳ | پایگاه داده MySQL برای استفاده. MySQL یک SGBD است که پایگاههای داده رابطهای را مدیریت میکند، که مجموعهای از جداول مرتبط با یکدیگر از طریق روابط هستند. در اینجا، نام پایگاه داده مدیریتشده مشخص میشود. |
4 | نام کاربری با حقوق دسترسی به این پایگاه داده |
۵ | رمز عبور آنها |
4.2. برنامه مالیات: کلاس ImpotsDSN
برنامهٔ ما بر پایهٔ کلاس PHP ImpotsDSN خواهد بود که دارای ویژگیها، سازنده و متد زیر است:
<?php
...
// ویژگیها
var $limites; // جدول محدودیتها
var $coeffR; // جدول coeffR
var $coeffN; //جدول coeffN
var $erreurs; // جدول خطا
- ما در بیانیه مسئله دیدیم که به سه مجموعه داده نیاز داشتیم
۱۲۶۲۰٫۰ | 0 | 0 |
13190 | 0.05 | ۶۳۱ |
۱۵٬۶۴۰ | 0.1 | ۱٬۲۹۰٫۵ |
۲۴٬۷۴۰ | 0.15 | ۲٬۰۷۲٫۵ |
۳۱٬۸۱۰ | 0.2 | ۳٬۳۰۹٫۵ |
۳۹٬۹۷۰ | 0.25 | ۴٬۹۰۰ |
۴۸٬۳۶۰ | 0.3 | ۶۸۹۸.۵ |
۵۵٬۷۹۰ | 0.35 | ۹٬۳۱۶٫۵ |
۹۲٬۹۷۰ | 0.4 | ۱۲٬۱۰۶ |
۱۲۷٬۸۶۰ | 0.45 | ۱۶٬۷۵۴٫۵ |
۱۵۱٬۲۵۰ | 0.50 | ۲۳٬۱۴۷٫۵ |
۱۷۲٬۰۴۰ | 0.55 | ۳۰٬۷۱۰ |
۱۹۵٬۰۰۰ | 0.60 | ۳۹٬۳۱۲ |
0 | 0.65 | ۴۹٬۰۶۲ |
ستون اول در ویژگی $limites کلاس ImotsDSN، ستون دوم در ویژگی $coeffR و ستون سوم در ویژگی $coeffN قرار داده میشود. این سه ویژگی توسط سازنده کلاس مقداردهی اولیه میشوند. سازنده دادهها را از یک منبع داده به نام ODBC بازیابی میکند. خطاهای مختلفی ممکن است در حین ساخت شی رخ دهد. این خطاها در ویژگی $erreurs به صورت آرایهای از پیامهای خطا گزارش میشوند.
- سازنده کلاس یک دیکشنری $impots را بهعنوان پارامتر دریافت میکند که شامل فیلدهای زیر است:
// dsn: نام DSN منبع داده ODBC حاوی مقادیر جداول محدودیت، coeffR, coeffN
// user: نام کاربری با دسترسی خواندن به منبع داده ODBC
// pwd: رمز عبور آنها
// جدول: نام جدولی که حاوی مقادیر است (محدودیتها، coeffR، coeffN)
// limits: نام ستونی که حاوی مقادیر محدودیت است
// coeffR: نام ستونی که حاوی مقادیر coeffR است
// coeffN: نام ستونی که حاوی مقادیر است coeffN
پارامتر سازنده تمام اطلاعات مورد نیاز برای خواندن دادهها از منبع داده ODBC را فراهم میکند
- پس از ایجاد شیء ImpotsDSN، کاربران کلاس قادر خواهند بود متد calculerImpots را فراخوانی کنند. این متد یک دیکشنری $personne را بهعنوان پارامتر میپذیرد:
// $person["marié"]: بله، نه
// $person["enfants"]: تعداد فرزندان
// $person["salaire"]: حقوق سالانه
کلاس کامل به شرح زیر است:
<?php
// تعریف یک کلاس objImpots
class ImpotsDSN{
// ویژگیها: ۳ جدول داده
var $limites; // جدول محدودیتها
var $coeffR; // جدول coeffR
var $coeffN; //جدول coeffN
var $erreurs; // جدول خطا
// سازنده
function ImpotsDSN($impots){
// $impots: فرهنگ لغت حاوی فیلدهای زیر
//dsn: نام DSN منبع داده ODBC حاوی مقادیر جداول محدودیت، coeffR، coeffN
// user: نام کاربری با دسترسی خواندن به منبع ODBC
// رمز عبور: رمز عبور آنها
// جدول: نام جدولی که حاوی مقادیر است (محدودیتها، coeffR، coeffN)
// limits: نام ستونی که مقادیر محدودیت را در خود دارد
// coeffR: نام ستونی که حاوی مقادیر coeffR است
// coeffN: نام ستونی که حاوی مقادیر است coeffN
// در ابتدا هیچ خطایی وجود ندارد
$this->erreurs=array();
// تأیید تماس
if (! isset($impots[dsn]) || ! isset($impots[user]) ||! isset($impots[pwd]) ||! isset($impots[table]) ||
! isset($impots[limites]) ||! isset($impots[coeffR]) ||! isset($impots[coeffN])){
// خطا
$this->erreurs[]="Appel incorrect";
//پایان
return;
}//اگر
// باز کردن پایگاه داده DSN
$connexion=odbc_connect($impots[dsn],$impots[user],$impots[pwd]);
//خطا؟
if(! $connexion){
//خطا
$this->erreurs[]="Impossible d'ouvrir la base DSN [$impots[dsn]] (".odbc_error().")";
//پایان
return;
}//اگر
// پرسوجو به پایگاه داده ارسال شد
$requête=odbc_prepare($connexion,"select $impots[limites],$impots[coeffR],$impots[coeffN] from $impots[table]");
if(! odbc_execute($requête)){
// خطا
$this->erreurs[]="Impossible d'expoiter la base DSN [$impots[dsn]] (".odbc_error().")";
// پایان
odbc_close($connexion);
return;
}//if
//پردازش نتایج پرسوجو
$this->limites=array();
$this->coeffR=array();
$this->coeffN=array();
while(odbc_fetch_row($requête)){
// یک ردیف دیگر
$this->limites[]=odbc_result($requête,$impots[limites]);
$this->coeffR[]=odbc_result($requête,$impots[coeffR]);
$this->coeffN[]=odbc_result($requête,$impots[coeffN]);
}//در حالی که
// بستن پایگاه داده
odbc_close($connexion);
}//سازنده
// --------------------------------------------------------------------------
function calculer($personne){
// $person["marié"]: بله، نه
// $person["enfants"]: تعداد فرزندان
// $person["salaire"] : حقوق سالانه
//آیا کالا در وضعیت خوبی است؟
if (! is_array($this->erreurs) || count($this->erreurs)!=0) return -1;
// تأیید تماس
if (! isset($personne[marié]) || ! isset($personne[enfants]) ||! isset($personne[salaire])){
// خطا
$this->erreurs[]="Appel incorrect";
//پایان
return -1;
}//اگر
//آیا پارامترها صحیح هستند؟
$personne[marié]=strtolower($personne[marié]);
if($personne[marié]!=oui && $personne[marié]!=non){
//خطا
$this->erreurs[]="Statut marital [$personne[marié]] incorrect";
}//if
if(! preg_match("/^\s*\d{1,3}\s*$/",$personne[enfants])){
// خطا
$this->erreurs[]="Nombre d'enfants [$personne[enfants]] incorrect";
}//if
if(! preg_match("/^\s*\d+\s*$/",$personne[salaire])){
//خطا
$this->erreurs[]="salaire [$personne[salaire]] incorrect";
}//if
// آیا خطایی وجود دارد؟
if(count($this->erreurs)!=0) return -1;
// تعداد وعدهها
if($personne[marié]==oui) $nbParts=$personne[enfants]/2+2;
else $nbParts=$personne[enfants]/2+1;
// نیم سهم اضافی اگر حداقل ۳ فرزند وجود داشته باشد
if($personne[enfants]>=3) $nbParts+=0.5;
//درآمد مشمول مالیات
$revenuImposable=0.72*$personne[salaire];
// ضریب خانوادگی
$quotient=$revenuImposable/$nbParts;
//در انتهای جدول محدودیتها قرار میگیرد تا حلقهٔ زیر را خاتمه دهد
$this->limites[$this->nbLimites]=$quotient;
//محاسبه مالیات
$i=0;
while($quotient>$this->limites[$i]) $i++;
// زیرا $quotient در انتهای جدول $limites، حلقه قبلی قرار داده شده است
//نمیتواند از آرایه $limites فراتر رود
//اکنون میتوانیم مالیات را محاسبه کنیم
return floor($revenuImposable*$this->coeffR[$i]-$nbParts*$this->coeffN[$i]);
}//calculImpots
}//کلاس مالیات
?>
یک برنامه آزمایشی ممکن است به این شکل باشد:
<?php
//کتابخانهها
include "ImpotsDSN.php";
//ایجاد یک شیء مالیات
$conf=array(dsn=>"mysql-dbimpots",user=>admimpots,pwd=>mdpimpots,
table=>impots,limites=>limites,coeffR=>coeffR,coeffN=>coeffN);
$objImpots=new ImpotsDSN($conf);
// خطاهایی؟
if(count($objImpots->erreurs)!=0){
// پیام
echo "Les erreurs suivantes se sont produites :\n";
$erreurs=$objImpots->erreurs;
for($i=0;$i<count($erreurs);$i++){
echo "$erreurs[$i]\n";
}//برای
// پایان
exit(1);
}//اگر
// آزمایشها
calculerImpots($objImpots,oui,2,200000);
calculerImpots($objImpots,non,2,200000);
calculerImpots($objImpots,oui,3,200000);
calculerImpots($objImpots,non,3,200000);
calculerImpots($objImpots,array(),array(),array());
// end
exit(0);
// -----------------------------------------------------------
function calculerImpots($objImpots,$marié,$enfants,$salaire){
// اکو
echo "impots($marié,$enfants,$salaire)\n";
//محاسبه مالیات
$personne=array(marié=>$marié,enfants=>$enfants,salaire=>$salaire);
$montant=$objImpots->calculer($personne);
// خطایی وجود دارد؟
if(count($objImpots->erreurs)!=0){
// پیام
echo "Les erreurs suivantes se sont produites :\n";
$erreurs=$objImpots->erreurs;
for($i=0;$i<count($erreurs);$i++){
echo "$erreurs[$i]\n";
}//برای
}else echo "montant=$montant\n";
}//calculerImp
- برنامه آزمون اطمینان حاصل میکند که فایل حاوی کلاس ImpotsDSN را «شامل» میکند
- و سپس با ارسال اطلاعات لازم به سازندهٔ شیء، یک شیء objImpots از کلاس ImpotsDSN ایجاد میکند
- پس از ایجاد این شیء، متد calculer آن پنج بار فراخوانی خواهد شد
نتایج بهدستآمده به شرح زیر است:
dos>e:\php43\php.exe test.php
impots(oui,2,200000)
montant=22504
impots(non,2,200000)
montant=33388
impots(oui,3,200000)
montant=16400
impots(non,3,200000)
montant=22504
impots(Array,Array,Array)
Les erreurs suivantes se sont produites :
Statut marital [array] incorrect
Nombre d'enfants [Array] incorrect
salaire [Array] incorrect
اکنون از کلاس ImpotsDSN بدون تکرار تعریف آن استفاده خواهیم کرد.
4.3. برنامه مالیات: نسخه ۱
ما اکنون نسخهٔ ۱ از برنامهٔ IMPOTS را ارائه میدهیم. زمینهٔ آن یک برنامهٔ وب است که رابط HTML را به کاربر نمایش میدهد تا سه پارامتر مورد نیاز برای محاسبهٔ مالیات را بهدست آورد:
- وضعیت تأهل (متأهل یا مجرد)
- تعداد فرزندان
- حقوق سالانه

این فرم توسط صفحهٔ زیرین، PHP، نمایش داده میشود:
<?php //فرم مالیات ?>
<html>
<head>
<title>impots</title>
<script language="JavaScript" type="text/javascript">
function effacer(){
// ریست فرم
with(document.frmImpots){
optMarie[0].checked=false;
optMarie[1].checked=true;
txtEnfants.value="";
txtSalaire.value="";
txtImpots.value="";
}//با
}//پاک کردن
</script>
</head>
<body background="/poly/impots/images/standard.jpg">
<center>
Calcul d'impôts
<hr>
<form name="frmImpots" method="POST">
<table>
<tr>
<td>Etes-vous marié(e)</td>
<td>
<input type="radio" name="optMarie" value="oui" <?php echo $requête->chkoui ?>>oui
<input type="radio" name="optMarie" value="non" <?php echo $requête->chknon ?>>non
</td>
</tr>
<tr>
<td>Nombre d'enfants</td>
<td><input type="text" size="5" name="txtEnfants" value="<?php echo $requête->enfants ?>"></td>
</tr>
<tr>
<td>Salaire annuel</td>
<td><input type="text" size="10" name="txtSalaire" value="<?php echo $requête->salaire ?>"></td>
</tr>
<tr>
<td><font color="green">Impôt</font></td>
<td><input type="text" size="10" name="txtImpots" value="<?php echo $requête->impots ?>" readonly></td>
</tr>
<tr></tr>
<tr>
<td><input type="submit" value="Calculer"></td>
<td><input type="button" value="Effacer" onclick="effacer()"></td>
</tr>
</table>
</form>
</center>
<?php
//آیا خطایی وجود دارد؟
if(count($requête->erreurs)!=0){
//نمایش خطاها
echo "<hr>\n<font color=\"red\">\n";
echo "Les erreurs suivantes se sont produites<br>";
echo "<ul>";
for($i=0;$i<count($requête->erreurs);$i++){
echo "<li>".$requête->erreurs[$i]."</li>\n";
}
echo "</ul>\n</font>\n";
}//اگر
?>
</body>
</html>
صفحه PHP صرفاً اطلاعاتی را که توسط برنامه اصلی اپلیکیشن در متغیر $requête به آن ارسال شده است نمایش میدهد، که این متغیر یک شیء با فیلدهای زیر است:
ویژگیهای دکمه رادیویی 'yes' و 'no' – میتوانند مقادیر "checked" یا "" را برای فعال یا غیرفعال کردن دکمه رادیویی مربوطه بگیرند | |
تعداد فرزندان مؤدی | |
حقوق سالانه آنها | |
مبلغ مالیات پرداختنی | |
فهرستی از هرگونه خطا – ممکن است خالی باشد. |
صفحهای که برای کلاینت ارسال میشود شامل یک اسکریپت جاوااسکریپت است که تابعی به نام effacer را در خود دارد که با دکمه «Effacer» مرتبط است و هدف آن بازنشانی فرم به وضعیت اولیه است: دکمه بدون تیک و فیلدهای ورودی خالی. شایان ذکر است که این نتیجه را نمیتوان با دکمه HTML از نوع «reset» به دست آورد. دلیل آن این است که وقتی از این نوع دکمه استفاده میشود، مرورگر فرم را به حالتی که دریافت کرده بود، ریست میکند. با این حال، در برنامه ما، مرورگر فرمهایی را دریافت میکند که ممکن است خالی نباشند.
برنامهای که فرم قبلی را پردازش میکند به شرح زیر است:
<?php
// فرم مالیاتی را پردازش میکند
//کتابخانهها
include "ImpotsDSN.php";
//پیکربندی برنامه
ini_set("register_globals","off");
ini_set("display_errors","off");
$formulaireImpots="impots_form.php";
$erreursImpots="impots_erreurs.php";
$bdImpots=array(dsn=>"mysql-dbimpots",user=>admimpots,pwd=>mdpimpots,
table=>impots,limites=>limites,coeffR=>coeffR,coeffN=>coeffN);
// بازیابی پارامترها
$requête->marié=$_POST["optMarie"];
$requête->enfants=$_POST["txtEnfants"];
$requête->salaire=$_POST["txtSalaire"];
//آیا همه پارامترها را داریم؟
if(! isset($requête->marié) || ! isset($requête->enfants) || ! isset($requête->salaire)){
//آمادهسازی یک فرم خالی
$requête->chkoui="";
$requête->chknon="checked";
$requête->enfants="";
$requête->salaire="";
$requête->impots="";
$requête->erreurs=array();
// نمایش فرم
include $formulaireImpots;
// پایان
exit(0);
}//اگر
//بررسی پارامترها
$requête=vérifier($requête);
// خطاها؟
if(count($requête->erreurs)!=0){
// نمایش فرم
include "$formulaireImpots";
// پایان
exit(0);
}//اگر
// محاسبه پاسخ
$requête=calculerImpots($bdImpots,$requête);
// آیا خطایی وجود دارد؟
if(count($requête->erreurs)!=0){
// نمایش فرم خطا
include "$erreursImpots";
// پایان
exit(0);
}//اگر
// نمایش فرم
include "$formulaireImpots";
// پایان
exit(0);
// --------------------------------------------------------
function vérifier($requête){
//اعتبار پارامترهای درخواست را بررسی میکند
// در ابتدا بدون خطا
$requête->erreurs=array();
//آیا وضعیت معتبر است؟
$requête->marié=strtolower($requête->marié);
if($requête->marié!="oui" && $requête->marié!="non"){
// یک خطا
$requête->erreurs[]="Statut marital [$requête->marié] incorrect";
}
//آیا تعداد فرزندان معتبر است؟
if(! preg_match("/^\s*\d+\s*$/",$requête->enfants)){
// یک خطا
$requête->erreurs[]="Nombre d'enfants [$requête->enfants] incorrect";
}
//آیا حقوق معتبر است؟
if(! preg_match("/^\s*\d+\s*$/",$requête->salaire)){
// یک خطا
$requête->erreurs[]="Salaire [$requête->salaire] incorrect";
}
//وضعیت دکمه رادیویی
if($requête->marié=="oui"){
$requête->chkoui="checked";
$requête->chknon="";
}else{
$requête->chknon="checked";
$requête->chkoui="";
}
//ارسال درخواست
return $requête;
}//بررسی
// --------------------------------------------------------
function calculerImpots($bdImpots,$requête){
// مبلغ مالیات را محاسبه میکند
//$bdImpots: فرهنگ لغت حاوی اطلاعات مورد نیاز برای خواندن منبع داده ODBC
//$requête: پرسوجویی حاوی اطلاعات مربوط به مؤدی
//یک شیء ایجاد میشود: ImpotsDSN
$objImpots=new ImpotsDSN($bdImpots);
// آیا خطایی وجود دارد؟
if(count($objImpots->erreurs)!=0){
//خطاها در پرسوجو گنجانده شدهاند
$requête->erreurs=$objImpots->erreurs;
//انجام شد
return $requête;
}//اگر
//محاسبه مالیات
$personne=array(marié=>"$requête->marié",enfants=>"$requête->enfants",salaire=>"$requête->salaire");
$requête->impots=$objImpots->calculer($personne);
// نتیجه را بازگردانید
return $requête;
}//calculerImpots
توضیحات:
- ابتدا کلاس ImpotsDSN وارد میشود. این پایه محاسبات مالیاتی را تشکیل میدهد و دسترسی به پایگاه داده را از ما پنهان میکند
- تعدادی راهاندازی برای تسهیل نگهداری برنامه انجام میشود. اگر هر پارامتری تغییر کند، مقادیر آن در این بخش بهروزرسانی خواهند شد. به طور کلی، این مقادیر پیکربندی در یک فایل یا پایگاه داده ذخیره میشوند.
- ما سه پارامتر را از فرم که مربوط به فیلدهای HTML، optMarie، txtEnfants و txtSalaire هستند، بازیابی میکنیم.
- این پارامترها ممکن است به طور کامل یا جزئی وجود نداشته باشند. این وضعیت به ویژه زمانی رخ میدهد که فرم برای اولین بار درخواست شود. در این حالت، یک فرم خالی ارسال میشود.
- سپس اعتبار سه پارامتر بازیابیشده بررسی میشود. اگر نادرست تشخیص داده شوند، فرم بدون تغییر اما همراه با یک لیست خطا بازگردانده میشود.
- پس از تأیید اعتبار سه پارامتر، مالیات قابل محاسبه است. این محاسبه توسط تابع calculerImpots انجام میشود. این تابع یک شیء از نوع ImpotsDSN ایجاد میکند و از متد calculer آن شیء استفاده میکند.
- ایجاد شیء ImpotsDSN ممکن است در صورتی که پایگاه داده در دسترس نباشد، با شکست مواجه شود. در این صورت، برنامه یک صفحه خطای خاص را نمایش میدهد که در آن خطاهای رخداده فهرست شدهاند.
- اگر همه چیز به درستی پیش برود، فرم همانطور که وارد شده بازگردانده میشود، اما با افزودن مبلغ مالیات قابل پرداخت.
صفحهٔ خطا به شرح زیر است:
<?php // صفحه خطا ?>
<html>
<head>
<title>Application impôts indisponible</title>
</head>
<body background="/poly/impots/images/standard.jpg">
<h3>Calcul d'impôts</h3>
<hr>
Application indisponible. Recommencez ultérieurement.<br><br>
<font color="red">
Les erreurs suivantes se sont produites<br>
<ul>
<?php
// نمایش خطا
for($i=0;$i<count($requête->erreurs);$i++){
echo "<li>".$requête->erreurs[$i]."</li>\n";
}
?>
</ul>
</font>
</body>
</html>
در اینجا چند مثال از خطاها آورده شده است. اولاً، رمز عبور نادرست وارد شده است:

دادههای نادرست در فرم وارد میشوند:

در نهایت، مقادیر صحیح وارد میشوند:

4.4. درخواست مالیات: نسخه ۲
در مثال قبلی، سرور اعتبار پارامترهای فرم txtEnfants و txtSalaire را بررسی میکند. در اینجا پیشنهاد میکنیم آنها را با استفاده از یک اسکریپت جاوااسکریپت تعبیهشده در صفحهٔ فرم اعتبارسنجی کنیم. سپس مرورگر اعتبارسنجی پارامترها را انجام میدهد. سرور تنها در صورتی تماس گرفته میشود که پارامترها معتبر باشند. این کار در مصرف پهنایباند صرفهجویی میکند. صفحهٔ نمایش impots-form.php به شکل زیر درمیآید:
<?php //فرم مالیاتی ?>
<html>
<head>
<title>impots</title>
<script language="JavaScript" type="text/javascript">
function effacer(){
........
}//پاک کردن
//------------------------------
function calculer(){
//بررسی تنظیمات قبل از ارسال آنها به سرور
with(document.frmImpots){
//تعداد فرزندان
champs=/^\s*(\d+)\s*$/.exec(txtEnfants.value);
if(champs==null){
//قالب اعتبارسنجی نشده است
alert("Le nombre d'enfants n'a pas été donné ou est incorrect");
nbEnfants.focus();
return;
}//اگر
//حقوق
champs=/^\s*(\d+)\s*$/.exec(txtSalaire.value);
if(champs==null){
//قالب تأیید نشده است
alert("Le salaire n'a pas été donné ou est incorrect");
salaire.focus();
return;
}//اگر
//OK – فرم در حال ارسال به سرور است
submit();
}//با
}//محاسبه
</script>
</head>
<body background="/poly/impots/images/standard.jpg">
............
<td><input type="button" value="Calculer" onclick="calculer()"></td>
<td><input type="button" value="Effacer" onclick="effacer()"></td>
........
</body>
</html>
لطفاً تغییرات زیر را توجه فرمایید:
- دکمه Calculer دیگر از نوع submit نیست، بلکه از نوع button است که با تابع calculer مرتبط است. این تابع است که فیلدهای txtEnfants و txTsalaire را تحلیل میکند. اگر صحیح باشند، مقادیر فرم به سرور ارسال (submit) میشوند؛ در غیر این صورت، یک پیام خطا نمایش داده میشود.
در اینجا مثالی از آنچه در صورت بروز خطا نمایش داده میشود، آورده شده است:

4.5. برنامه مالیات: نسخه ۳
ما در حال ایجاد یک تغییر جزئی در برنامه هستیم تا مفهوم «سشن» را معرفی کنیم. اکنون این برنامه را یک شبیهساز محاسبه مالیات در نظر میگیریم. کاربر میتواند «تنظیمات» مختلف مالیاتدهنده را شبیهسازی کرده و ببیند که مالیات پرداختنی برای هر یک چه خواهد بود. صفحه وب زیر نمونهای از آنچه ممکن است نمایش داده شود را ارائه میدهد:

برنامه برای نمایش صفحه بالا به شرح زیر است:
<?php //فرم مالیاتی ?>
<html>
<head>
<title>impots</title>
<script language="JavaScript" type="text/javascript">
function effacer(){
...
}//حذف
//------------------------------
function calculer(){
...
}//محاسبه
</script>
</head>
<body background="/poly/impots/images/standard.jpg">
<center>
Calcul d'impôts
<hr>
<form name="frmImpots" method="POST">
<table>
<tr>
<td>Etes-vous marié(e)</td>
<td>
<input type="radio" name="optMarie" value="oui" <?php echo $requête[chkoui] ?>>oui
<input type="radio" name="optMarie" value="non" <?php echo $requête[chknon] ?>>non
</td>
</tr>
<tr>
<td>Nombre d'enfants</td>
<td><input type="text" size="5" name="txtEnfants" value="<?php echo $requête[enfants] ?>"></td>
</tr>
<tr>
<td>Salaire annuel</td>
<td><input type="text" size="10" name="txtSalaire" value="<?php echo $requête[salaire] ?>"></td>
</tr>
<tr>
<td><font color="green">Impôt</font></td>
<td><input type="text" size="10" name="txtImpots" value="<?php echo $requête[impots] ?>" readonly></td>
</tr>
<tr></tr>
<tr>
<td><input type="button" value="Calculer" onclick="calculer()"></td>
<td><input type="button" value="Effacer" onclick="effacer()"></td>
</tr>
</table>
</form>
</center>
<?php
//آیا خطایی وجود دارد؟
if(count($requête[erreurs])!=0){
//نمایش خطاها
echo "<hr>\n<font color=\"red\">\n";
echo "Les erreurs suivantes se sont produites<br>";
echo "<ul>";
for($i=0;$i<count($requête[erreurs]);$i++){
echo "<li>".$requête[erreurs][$i]."</li>\n";
}
echo "</ul>\n</font>\n";
}//اگر
//آیا شبیهسازیهایی وجود دارد؟
else if(count($requête[simulations])!=0){
//نمایش شبیهسازیها
echo "<hr>\n<h2>Résultats des simulations</h2>\n";
echo "<table border=\"1\">\n";
echo "<tr><td>Marié</td><td>Enfants</td><td>Salaire annuel (F)</td><td>Impôts à payer (F)</td></tr>\n";
for($i=0;$i<count($requête[simulations]);$i++){
echo "<tr>".
"<td>".$requête[simulations][$i][0]."</td>".
"<td>".$requête[simulations][$i][1]."</td>".
"<td>".$requête[simulations][$i][2]."</td>".
"<td>".$requête[simulations][$i][3]."</td>".
"</tr>\n";
}//برای
echo "</table>\n";
}//اگر
?>
</body>
</html>
برنامه نمایش تفاوتهای جزئی با نسخه قبلی خود دارد:
- واژهنامه به جای یک شیء $requête، یک واژهنامه $requête دریافت میکند. بنابراین ما $requête[enfants] را مینویسیم و نه $requête->children.
- این دیکشنری دارای یک فیلد به نام simulations است که یک آرایه دو بعدی است. $requête[simulations][i] شبیهسازی شمارهٔ i است. این خود یک آرایه از چهار رشتهٔ متنی است: وضعیت تأهل، تعداد فرزندان، حقوق سالانه، مالیات پرداختنی.
از سوی دیگر، برنامهٔ اصلی تغییرات مهمتری را پشت سر گذاشته است:
<?php
// فرم مالیاتی را پردازش میکند
//کتابخانهها
include "ImpotsDSN.php";
// شروع جلسه
session_start();
//پیکربندی برنامه
ini_set("register_globals","off");
ini_set("display_errors","off");
$formulaireImpots="impots_form.php";
$erreursImpots="impots_erreurs.php";
$bdImpots=array(dsn=>"mysql-dbimpots",user=>admimpots,pwd=>mdpimpots,
table=>impots,limites=>limites,coeffR=>coeffR,coeffN=>coeffN);
//بازیابی پارامترهای جلسه
$session=$_SESSION["session"];
//آیا جلسه معتبر است؟
if(! isset($session) || ! isset($session[objImpots]) || ! isset($session[simulations])){
// شروع یک جلسه جدید
$session=array(objImpots=>new ImpotsDSN($bdImpots),simulations=>array());
// آیا خطایی وجود دارد؟
if(count($session[objImpots]->erreurs)!=0){
$requête=array(erreurs=>$session[objImpots]->erreurs);
// نمایش صفحهٔ خطا
include $erreursImpots;
// پایان
$session=array();
terminerSession($session);
}//اگر
}//اگر
// بازیابی پارامترها برای تراکنش جاری
$requête[marié]=$_POST["optMarie"];
$requête[enfants]=$_POST["txtEnfants"];
$requête[salaire]=$_POST["txtSalaire"];
//آیا همه پارامترها را داریم؟
if(! isset($requête[marié]) || ! isset($requête[enfants]) || ! isset($requête[salaire])){
//نمایش فرم خالی
$requête=array(chkoui=>"",chknon=>"checked",enfants=>"",salaire=>"",impots=>"",
erreurs=>array(),simulations=>array());
include $formulaireImpots;
//پایان
terminerSession($session);
}//اگر
// بررسی پارامترها
$requête=vérifier($requête);
// آیا خطایی وجود دارد؟
if(count($requête[erreurs])!=0){
// نمایش فرم
include "$formulaireImpots";
// پایان
terminerSession($session);
}//اگر
// محاسبه مالیات پرداختنی
$requête[impots]=$session[objImpots]->calculer(array(marié=>$requête[marié],
enfants=>$requête[enfants],salaire=>$requête[salaire]));
// شبیهسازی دیگر
$session[simulations][]=array($requête[marié],$requête[enfants],$requête[salaire],$requête[impots]);
$requête[simulations]=$session[simulations];
// نمایش فرم
include "$formulaireImpots";
//پایان
terminerSession($session);
// --------------------------------------------------------
function vérifier($requête){
//اعتبار پارامترهای پرسوجو را بررسی میکند
// در ابتدا هیچ خطایی وجود ندارد
$requête[erreurs]=array();
//وضعیت معتبر؟
$requête[marié]=strtolower($requête[marié]);
if($requête[marié]!="oui" && $requête[marié]!="non"){
// یک خطا
$requête[erreurs][]="Statut marital [$requête[marié]] incorrect";
}
//آیا تعداد فرزندان معتبر است؟
if(! preg_match("/^\s*\d+\s*$/",$requête[enfants])){
// یک خطا
$requête[erreurs][]="Nombre d'enfants [$requête[enfants]] incorrect";
}
// حقوق معتبر؟
if(! preg_match("/^\s*\d+\s*$/",$requête[salaire])){
// یک خطا
$requête[erreurs][]="Salaire [$requête[salaire]] incorrect";
}
//وضعیت دکمه رادیویی
if($requête[marié]=="oui"){
$requête[chkoui]="checked";
$requête[chknon]="";
}else{
$requête[chknon]="checked";
$requête[chkoui]="";
}
//ارسال درخواست
return $requête;
}//بررسی
// --------------------------------------------------------
function terminerSession($session){
// ذخیرهٔ جلسه
$_SESSION[session]=$session;
// پایان اسکریپت
exit(0);
}//terminerSession
- ابتدا، این برنامه یک جلسه را مدیریت میکند. بنابراین، ما در ابتدای اسکریپت یک جلسه را آغاز میکنیم:
- این جلسه یک متغیر واحد را ذخیره میکند: دیکشنری $session. این دیکشنری دو فیلد دارد:
- objImpots: یک شیء ImpotsDSN. این شیء در جلسهٔ مشتری ذخیره میشود تا از دسترسیهای تکراری و غیرضروری به پایگاه دادهٔ ODBC جلوگیری شود.
- شبیهسازیها: آرایه شبیهسازیها، که برای نگهداری شبیهسازیهای مبادلات قبلی بین مبادلات استفاده میشود.
- به محض شروع جلسه، متغیر $session بازیابی میشود. اگر این متغیر هنوز وجود نداشته باشد، به این معنی است که جلسه در حال شروع است. سپس یک شیء ImpotsDSN و یک آرایه شبیهسازی خالی ایجاد میشوند. در صورت لزوم، خطاها گزارش میگردند.
<?php
...
//بازیابی پارامترهای جلسه
$session=$_SESSION["session"];
//آیا جلسه معتبر است؟
if(! isset($session) || ! isset($session[objImpots]) || ! isset($session[simulations])){
// شروع یک جلسه جدید
$session=array(objImpots=>new ImpotsDSN($bdImpots),simulations=>array());
// آیا خطایی وجود دارد؟
if(count($session[objImpots]->erreurs)!=0){
$requête=array(erreurs=>$session[objImpots]->erreurs);
// نمایش صفحهٔ خطا
include $erreursImpots;
// پایان
$session=array();
terminerSession($session);
}//اگر
}//اگر
- پس از راهاندازی موفقیتآمیز جلسه، پارامترهای تبادل بازیابی میشوند. اگر همه پارامترهای مورد انتظار موجود نباشند، یک فرم خالی ارسال میشود.
<?php
...
// بازیابی پارامترها برای تراکنش جاری
$requête[marié]=$_POST["optMarie"];
$requête[enfants]=$_POST["txtEnfants"];
$requête[salaire]=$_POST["txtSalaire"];
//آیا همه پارامترها را داریم؟
if(! isset($requête[marié]) || ! isset($requête[enfants]) || ! isset($requête[salaire])){
//نمایش فرم خالی
$requête=array(chkoui=>"",chknon=>"checked",enfants=>"",salaire=>"",impots=>"",
erreurs=>array(),simulations=>array());
include $formulaireImpots;
//پایان
terminerSession($session);
}//اگر
- اگر همه پارامترها صحیح باشند، تأیید میشوند. اگر خطایی وجود داشته باشد، گزارش میشود:
<?php
...
// بررسی پارامترها
$requête=vérifier($requête);
// آیا خطایی وجود دارد؟
if(count($requête[erreurs])!=0){
// نمایش فرم
include "$formulaireImpots";
// پایان
terminerSession($session);
}//اگر
- اگر پارامترها صحیح باشند، مالیات محاسبه میشود:
<?php
...
// محاسبه مالیات پرداختنی
$requête[impots]=$session[objImpots]->calculer(array(marié=>$requête[marié],
enfants=>$requête[enfants],salaire=>$requête[salaire]));
- شبیهسازی فعلی به جدول شبیهسازی اضافه میشود، که سپس به همراه فرم برای کلاینت ارسال میگردد:
<?php
...
// شبیهسازی دیگر
$session[simulations][]=array($requête[marié],$requête[enfants],$requête[salaire],$requête[impots]);
$requête[simulations]=$session[simulations];
//نمایش فرم
include "$formulaireImpots";
// پایان
terminerSession($session);
- در همه موارد، اسکریپت با ذخیره کردن متغیر $session در جلسه پایان مییابد. این کار در رویه terminerSession انجام میشود.
<?php
...
function terminerSession($session){
// ذخیره جلسه
$_SESSION[session]=$session;
//پایان اسکریپت
exit(0);
}//terminerSession
در پایان، برنامه نمایش خطاهای دسترسی به پایگاه داده (impots-erreurs.php) را معرفی میکنیم:
<?php // صفحهٔ خطا ?>
<html>
<head>
<title>Application impôts indisponible</title>
</head>
<body background="/poly/impots/images/standard.jpg">
<h3>Calcul d'impôts</h3>
<hr>
Application indisponible. Recommencez ultérieurement.<br><br>
<font color="red">
Les erreurs suivantes se sont produites<br>
<ul>
<?php
// نمایش خطا
for($i=0;$i<count($requête[erreurs]);$i++){
echo "<li>".$requête[erreurs][$i]."</li>\n";
}
?>
</ul>
</font>
</body>
</html>
در اینجا نیز شیء $requête با یک فرهنگ لغت $requête جایگزین شده است.
4.6. برنامه مالیات: نسخه ۴
اکنون یک برنامه مستقل ایجاد خواهیم کرد که بهعنوان کلاینت وب برای برنامه وب قبلی impots عمل میکند. این برنامه یک برنامه کنسول خواهد بود که از یک پنجره DOS اجرا میشود:
dos>e:\php43\php.exe cltImpots.php
Syntaxe cltImpots.php urlImpots marié enfants salaire [jeton]
تنظیمات کلاینت وب cltImpots به شرح زیر است:
// $0 نحو urlImpots توکن حقوق فرزندان متأهل
// مشتری یک سرویس مالیاتی که روی urlImpots اجرا میشود
// سه مورد اطلاعات زیر را به این سرویس ارسال میکند: وضعیت تأهل، تعداد فرزندان، حقوق
// احتمالاً همراه با توکن جلسه در صورت ارائه شدن
//جدول شبیهسازیها را برای جلسه نمایش میدهد
در اینجا چند مثال از نحوه استفاده از آن آورده شده است. اول، مثالی بدون توکن جلسه.
dos>e:\php43\php.exe cltImpots.php http://localhost/poly/impots/6/impots.php yes 2 200000
Jeton de session=[a6297317667bc981c462120987b8dd18]
Simulations :
[Marié,Enfants,Salaire annuel (F),Impôts à payer (F)]
[oui,2,200000,22504]
ما با موفقیت مبلغ مالیات بدهی (22,504 f) را بازیابی کردیم. ما همچنین توکن جلسه را بازیابی کردیم. اکنون میتوانیم از آن برای یک پرسوجوی دوم استفاده کنیم:
dos>e:\php43\php.exe cltImpots.php http://localhost/poly/impots/6/impots.php yes 3 200000 a6297317667bc981c462120987b8dd18
Jeton de session=[a6297317667bc981c462120987b8dd18]
Simulations :
[Marié,Enfants,Salaire annuel (F),Impôts à payer (F)]
[oui,2,200000,22504]
[oui,3,200000,16400]
ما در واقع جدول شبیهسازیهای ارسالشده توسط سرور وب را دریافت میکنیم. توکن بازیابیشده البته بدون تغییر باقی میماند. اگر در حالی که پایگاه داده راهاندازی نشده است، از سرویس وب استعلام بگیریم، نتیجه زیر را دریافت میکنیم:
dos>e:\php43\php.exe cltImpots2.php http://localhost/poly/impots/6/impots.php بله ۳,۰۰۰,۰۰۰
Jeton de session=[8369014d5053212bc42f64bbdfb152ee]
Les erreurs suivantes se sont produites :
Impossible d'ouvrir la base DSN [mysql-dbimpots] (S1000)
هنگام نوشتن یک کلاینت وب سفارشی، ضروری است که دقیقاً بدانیم سرور در پاسخ به درخواستهای مختلف ممکن از سوی کلاینت، چه چیزی ارسال میکند. سرور مجموعهای از خطوط را ارسال میکند، مانند HTML، که حاوی اطلاعات مفید و عناصر دیگری است که صرفاً برای اهداف چیدمان وجود دارند، مانند HTML. بیانهای منظم (Regular expressions) مانند PHP میتوانند به ما در شناسایی اطلاعات مفید در میان جریان خطوط ارسالشده توسط سرور کمک کنند. برای این کار، باید فرمت دقیق پاسخهای مختلف سرور را بدانیم. برای پی بردن به این موضوع، میتوانیم با استفاده از یک مرورگر از سرویس وب استعلام بگیریم و کد منبع بازگشتی را بررسی کنیم. شایان ذکر است که این روش به ما اجازه نمیدهد تا سربرگها را در پاسخ وب سرور شناسایی کنیم. دانستن این موارد گاهی اوقات مفید است. سپس میتوانیم از یکی از دو کلاینت وب عمومی که در فصل قبلی به آنها پرداخته شد، استفاده کنیم.
اگر مثالهای قبلی را تکرار کنیم، کد HTML دریافتی توسط مرورگر برای جدول شبیهسازی به این صورت است:
<h2>Résultats des simulations</h2>
<table border="1">
<tr><td>Marié</td><td>Enfants</td><td>Salaire annuel (F)</td><td>Impôts à payer (F)</td></tr>
<tr><td>oui</td><td>2</td><td>200000</td><td>22504</td></tr>
<tr><td>oui</td><td>3</td><td>200000</td><td>16400</td></tr>
</table>
این یک جدول با نام HTML است که تنها جدول ارسالشده در سند میباشد. بنابراین، عبارت منظم "|<tr>\s*<td>(.+?)</td>\s*<td>(.+?)</td>\s*<td>(.+?)</td>\s*<td>(.+?)</td>\s*</tr>|" باید امکان بازیابی شبیهسازیهای مختلف دریافتشده از سند را فراهم کند. هنگامی که پایگاه داده در دسترس نباشد، سند زیر دریافت میشود:
Application indisponible. Recommencez ultérieurement.<br><br>
<font color="red">
Les erreurs suivantes se sont produites<br>
<ul>
<li>Impossible d'ouvrir la base DSN [mysql-dbimpots] (S1000)</li>
</ul>
</font>
برای بازیابی خطا، کلاینت وب باید در پاسخ وبسرور خطوطی را که با الگوی "|<li>(.+?)</li>|" مطابقت دارند، جستجو کند.
اکنون دستورالعملهایی در مورد اقدام در هنگام درخواست کاربر برای محاسبه مالیات از کلاینت کنسول قبلی داریم:
- بررسی کنید که همه پارامترها معتبر هستند و در صورت لزوم، هرگونه خطا را گزارش کنید.
- به URL ارائهشده بهعنوان اولین پارامتر متصل شوید. برای این کار، از الگوی کلاینت وب عمومی که قبلاً ارائه و مورد بحث قرار گرفته است، پیروی خواهیم کرد.
- در جریان پاسخ سرور، از عبارتهای منظم برای انجام یکی از موارد زیر استفاده کنید:
- یافتن پیامهای خطا
- یافتن نتایج شبیهسازی
کد مشتری cltImpots.php به شرح زیر است:
<?php
// سینتکس $0 urlImpots توکن حقوق فرزندان متأهل
//یک مشتری از سرویس مالیاتی است که روی urlImpots اجرا میشود
//سه مورد اطلاعات زیر را به این سرویس ارسال میکند: وضعیت تأهل، تعداد فرزندان، حقوق
// ممکن است همراه با توکن جلسه، در صورت ارائه شدن
//جدول شبیهسازیها را برای جلسه نمایش میدهد
// تعداد آرگومانها را بررسی میکند
if(count($argv)!=5 && count($argv)!=6){
//خطا
fwrite(STDERR,"Syntaxe $argv[0] urlImpots marié enfants salaire [jeton]\n");
// پایان
exit(1);
}//اگر
// توجه به URL
$urlImpots=analyseURL($argv[1]);
if(isset($urlImpots[erreur])){
// خطا
fwrite(STDERR,"$urlImpots[erreur]\n");
// پایان
exit(1);
}//if
// تحلیل درخواست کاربر
$demande=analyseDemande("$argv[2],$argv[3],$argv[4]");
// خطاها؟
if($demande[erreur]){
// پیام
echo "$demande[erreur]\n";
//همینه
exit(1);
}//اگر
//درخواست قابل انجام است – توکن جلسه استفاده میشود
$impots=getImpots($urlImpots,$demande,$argv[5]);
// نمایش توکن جلسه
echo "Jeton de session=[$impots[jeton]]\n";
// آیا خطایی وجود دارد؟
if(count($impots[erreurs])!=0){
//پیامهای خطا
echo "Les erreurs suivantes se sont produites :\n";
for($i=0;$i<count($impots[erreurs]);$i++){
echo $impots[erreurs][$i]."\n";
}//برای
}else{
// بدون خطا – نمایش شبیهسازیها
echo "Simulations :\n";
for($i=0;$i<count($impots[simulations]);$i++){
echo "[".implode(",",$impots[simulations][$i])."]\n";
}//برای
}//اگر
//پایان برنامه
exit(0);
// --------------------------------------------------------------
function analyseURL($URL){
//اعتبار URL $URL را بررسی میکند
$url=parse_url($URL);
//پروتکل
if(strtolower($url[scheme])!="http"){
$url[erreur]="l'URL [$URL] n'est pas au format http://machine[:port][/chemin]";
return $url;
}//اگر
// ماشین
if(! isset($url[host])){
$url[erreur]="l'URL [$URL] n'est pas au format http://machine[:port][/chemin]";
return $url;
}//اگر
// پورت
if(! isset($url[port])) $url[port]=80;
// درخواست
if(isset($url["query"])){
$url[erreur]="l'URL [$URL] n'est pas au format http://machine[:port][/chemin]";
return $url;
}//if
//بازگشت
return $url;
}//analyseURL
// -----------------------------------------------------------
function analyseDemande($demande){
// $demande: رشتهای که باید تحلیل شود
//باید به فرمت: متأهل، فرزندان، حقوق باشد
// قالب معتبر
if(! preg_match("/^\s*(oui|non)\s*,\s*(\d{1,3})\s*,\s*(\d+)\s*$/i",$demande,$champs)){
// قالب نامعتبر
return(array(erreur=>"Format (marié, enfants, salaire) invalide."));
}
// درست است
$marié=strtolower($champs[1]);
return array(marié=>$marié,enfants=>$champs[2],salaire=>$champs[3]);
}//analyseDemande
// --------------------------------------------------------------
function getImpots($urlImpots,$demande,$jeton){
// $urlImpots: URL برای پرسوجو
// $demande: دیکشنری حاوی فیلدهای 'spouse', 'children' و 'salary'
//$jeton: یک توکن جلسه احتمالی
// برقراری اتصال روی پورت $urlImpots[port] از $urlImpots[host]
$connexion=fsockopen($urlImpots[host],$urlImpots[port],&$errno,&$erreur);
// در صورت خطا بازگرداندن
if(! $connexion){
return array(erreurs=>array("Echec de la connexion au site ($urlImpots[host],$urlImpots[port]) : $erreur"));
}//اگر
// سربرگها را به سرور ارسال کنید HTTP
POST($connexion,$urlImpots,$jeton,
array(optMarie=>$demande[marié],txtEnfants=>$demande[enfants],txtSalaire=>$demande[salaire]));
// پاسخ سرور را بخوانید – ابتدا سربرگها HTTP
// اولین خط
$ligne=fgets($connexion,10000);
// URL یافت شد؟
if(! preg_match("/^(.+?) 200 OK\s*$/",$ligne)){
//URL غیرقابل دسترسی – با خطا بازگرداندن
return array(erreurs=>array("L'URL $urlImpots[path] n'a pu être trouvée"));
}//اگر
// سربرگهای دیگر را بخوانید HTTP
while(($ligne=fgets($connexion,10000)) && (($ligne=rtrim($ligne))!="")){
// در صورت یافت نشدن توکن، آن را جستجو کنید
if(! $jeton){
// جستجو برای خط 'set-cookie'
if(preg_match("/^Set-Cookie: PHPSESSID=(.*?);/i",$ligne,$champs)){
//اگر توکن پیدا شد – آن را ذخیره کنید
$jeton=$champs[1];
}//اگر
}//اگر
}//خط بعدی
//در حال خواندن سند زیر
$document="";
while($ligne=fread($connexion,10000)){
$document.=$ligne;
}//در حالی که
//اتصال را ببندید
fclose($connexion);
//سند را تجزیه کن
$impots=getInfos($document);
// نتیجه را بازگردانید
return array(jeton=>$jeton,erreurs=>$impots[erreurs],simulations=>$impots[simulations]);
}//getImpots
// --------------------------------------------------------------
function POST($connexion,$url,$jeton,$paramètres){
//$connexion: اتصال به سرور وب
//$url: URL مورد پرسوجو
// $paramètres: فرهنگ پارامترهایی که باید ارسال شوند
// آمادهسازی POST
$post="";
while(list($paramètre,$valeur)=each($paramètres)){
$post.=$paramètre."=".urlencode($valeur)."&";
}//در حالی که
// حذف آخرین کاراکتر
$post=substr($post,0,-1);
//در حال آمادهسازی درخواست HTTP در قالب HTTP/1.0
$HTTP="POST $url[path] HTTP/1.0\n";
$HTTP.="Content-type: application/x-www-form-urlencoded\n";
$HTTP.="Content-length: ".strlen($post)."\n";
$HTTP.="Connection: close\n";
if($jeton) $HTTP.="Cookie: PHPSESSID=$jeton\n";
$HTTP.="\n";
$HTTP.=$post;
//درخواست HTTP را ارسال کنید
fwrite($connexion,$HTTP);
}//POST
// --------------------------------------------------------------
function getInfos($document){
//$document: سند HTML
//ما در جستجوی فهرست خطاها هستیم
// یا جدول شبیهسازی
//آمادهسازی نتیجه
$impots[erreurs]=array();
$impots[simulations]=array();
//آیا خطایی وجود دارد؟
// قالب خطای خط
$modErreur="|<li>(.+?)</li>|";
// جستجو در سند
if(preg_match_all($modErreur,$document,$champs,PREG_SET_ORDER)){
//بازیابی خطاها
for($i=0;$i<count($champs);$i++){
$impots[erreurs][]=$champs[$i][1];
}//برای
//تکمیل شد
return $impots;
}//اگر
// نمونهای از یک سطر در جدول شبیهسازی
// yes220000022504
$modSimulation="|<tr>\s*<td>(.+?)</td>\s*<td>(.+?)</td>\s*<td>(.+?)</td>\s*<td>(.+?)</td>\s*</tr>|";
//جستجو در سند
if(preg_match_all($modSimulation,$document,$champs,PREG_SET_ORDER)){
//بازیابی شبیهسازیها
for($i=0;$i<count($champs);$i++){
$impots[simulations][]=array($champs[$i][1],$champs[$i][2],$champs[$i][3],$champs[$i][4]);
}//برای
// بازگرداندن نتیجه
return $impots;
}//اگر
// عادی نیست که اینجا به پایان برسید
return $impots;
}//getInfos
?>
توضیحات:
- برنامه با بررسی اعتبار پارامترهای دریافتی آغاز میشود. برای این کار، از دو تابع استفاده میکند: analyseURL که اعتبار URL را بررسی میکند، و analyseDemande که سایر پارامترها را بررسی میکند.
- مالیات توسط تابع getImpots محاسبه میشود:
- تابع getImpots به صورت زیر تعریف شده است:
<?php
...
// --------------------------------------------------------------
function getImpots($urlImpots,$demande,$jeton){
// $urlImpots: URL برای پرسوجو
// $demande: دیکشنری حاوی فیلدهای «همسر»، «فرزندان» و «حقوق»
//$jeton: یک توکن جلسه احتمالی
- (ادامه)
- $urlImpots یک دیکشنری است که شامل فیلدهای زیر میباشد:
- میزبان: ماشینی که سرویس وب روی آن قرار دارد
- port: پورت سرویس آن
- path: مسیر به منبع درخواستشده
- $demande یک دیکشنری حاوی فیلدهای زیر است:
- married: بله/خیر: وضعیت تأهل
- children: تعداد فرزندان
- حقوق: حقوق سالانه
- $jeton توکن جلسه است.
- $urlImpots یک دیکشنری است که شامل فیلدهای زیر میباشد:
این تابع یک دیکشنری حاوی فیلدهای زیر را بازمیگرداند:
- errors: آرایهای از پیامهای خطا
- شبیهسازیها: آرایهای از شبیهسازیها، که هر شبیهسازی آرایهای از چهار عنصر است (ازدواج، فرزندان، حقوق، مالیات)
- token: توکن جلسه
- پس از به دست آمدن نتیجه getImpots، برنامه نتایج را نمایش داده و متوقف میشود:
<?php
...
// توکن جلسه نمایش داده میشود
echo "Jeton de session=[$impots[jeton]]\n";
// آیا خطایی وجود دارد؟
if(count($impots[erreurs])!=0){
//پیامهای خطا
echo "Les erreurs suivantes se sont produites :\n";
for($i=0;$i<count($impots[erreurs]);$i++){
echo $impots[erreurs][$i]."\n";
}//برای
}else{
// بدون خطا – شبیهسازیها نمایش داده شدند
echo "Simulations :\n";
for($i=0;$i<count($impots[simulations]);$i++){
echo "[".implode(",",$impots[simulations][$i])."]\n";
}//برای
}//اگر
//پایان برنامه
exit(0);
- اکنون بیایید تابع getImpots($urlImpots, $demande, $jeton) را که باید
- یک اتصال TCP-IP را روی پورت $urlImpots[port] ماشین $urlImpots[host] برقرار کند
- سربرگهای HTTP را به سرور وب ارسال کند، که بهویژه منتظر توکن جلسه است اگر توکنی وجود داشته باشد
- یک درخواست POST را با پارامترهای موجود در فرهنگ لغت $demande به سرور وب ارسال کنید
- پاسخ وبسرور را تحلیل کنید تا یا فهرستی از خطاها یا جدولی از شبیهسازیها را بیابید.
- سربرگهای HTTP با استفاده از تابع POST ارسال میشوند:
<?php
...
// HTTP سربرگها به سرور ارسال میشوند
POST($connexion,$urlImpots,$jeton,
array(optMarie=>$demande[marié],txtEnfants=>$demande[enfants],txtSalaire=>$demande[salaire]));
- پس از ارسال سربرگهای HTTP، تابع getImpots کل پاسخ سرور را میخواند و آن را در $document ذخیره میکند. پاسخ بدون تجزیه خوانده میشود، به جز خط اول که به ما میگوید آیا وبسرور URL مورد درخواست ما را پیدا کرده است یا خیر. در واقع، اگر URL پیدا شده باشد، وبسرور با HTTP/ پاسخ میدهد.1.X 200 OK، که در آن X به نسخهٔ HTTP مورد استفاده بستگی دارد.
<?php
...
// URL یافت شد؟
if(! preg_match("/^(.+?) 200 OK\s*$/",$ligne)){
<?php
...
//URL غیرقابل دسترسی – با خطا بازگرداندن
return array(erreurs=>array("L'URL $urlImpots[path] n'a pu être trouvée"));
}//اگر
- این سند با استفاده از تابع getInfos تحلیل میشود:
- نتیجهی بازگرداندهشده یک دیکشنری با دو فیلد است:
- errors: فهرست خطاها – ممکن است خالی باشد
- شبیهسازیها: فهرست شبیهسازیها – ممکن است خالی باشد
- با این حساب، تابع getImpots میتواند نتیجهٔ خود را به شکل یک دیکشنری بازگرداند.
<?php
...
// بازگرداندن نتیجه
return array(jeton=>$jeton,erreurs=>$impots[erreurs],simulations=>$impots[simulations]);
- اکنون تابع POST را تحلیل کنیم:
<?php
...
// --------------------------------------------------------------
function POST($connexion,$url,$jeton,$paramètres){
// $connexion: اتصال به سرور وب
// $url: URL برای پرسوجو
// $paramètres: فرهنگ واژگان پارامترهایی که باید ارسال شوند
// آمادهسازی POST
$post="";
while(list($paramètre,$valeur)=each($paramètres)){
$post.=$paramètre."=".urlencode($valeur)."&";
}//در حالی که
// حذف آخرین کاراکتر
$post=substr($post,0,-1);
//در حال آمادهسازی درخواست HTTP در قالب HTTP/1.0
$HTTP="POST $url[path] HTTP/1.0\n";
$HTTP.="Content-type: application/x-www-form-urlencoded\n";
$HTTP.="Content-length: ".strlen($post)."\n";
$HTTP.="Connection: close\n";
if($jeton) $HTTP.="Cookie: PHPSESSID=$jeton\n";
$HTTP.="\n";
$HTTP.=$post;
//درخواست HTTP را ارسال کنید
fwrite($connexion,$HTTP);
}//POST
در حالی که قبلاً این فرصت را داشتیم که از طریق GET یک منبع وب را درخواست کنیم، هنوز این فرصت را با POST نداشتیم. POST در نحوه ارسال پارامترها به سرور با GET متفاوت است. این باید سربرگهای زیر HTTP را ارسال کند:
با
- chemin: مسیر منبع وب درخواستشده، در این مورد $url[path]
- HTTP/1.X: پروتکل مورد نظر HTTP. در اینجا، HTTP/1.0 برای دریافت پاسخ در یک تکه واحد انتخاب شده است. HTTP/1.1 امکان ارسال پاسخ در چندین تکه (chunked) را فراهم میکند.
- N تعداد کاراکترهایی را که کلاینت قصد ارسال به سرور را دارد مشخص میکند.
حروف N که پارامترهای پرسوجو را تشکیل میدهند، بلافاصله پس از خط خالی که سربرگهای HTTP را به سرور ارسال میکند، ارسال میشوند. این پارامترها به شکل param1=val1¶m2=val2&... هستند، که در آن parami نام پارامتر و vali مقدار آن است. مقادیر معتبر ممکن است شامل کاراکترهای «مشکلساز» مانند فاصله، کاراکتر &، کاراکتر = و غیره باشند. این کاراکترها باید با رشته %XX جایگزین شوند، که در آن XX کد هگزادسیمال آنها است. تابع PHP urlencode این کار را انجام میدهد. عملیات معکوس توسط urldecode انجام میشود.
در نهایت باید توجه داشت که اگر توکنی وجود داشته باشد، با هدر HTTP و کوکی PHPSESSID=token ارسال میشود.
- اکنون باید تابع تحلیلکننده پاسخ سرور را بررسی کنیم:
<?php
...
// --------------------------------------------------------------
function getInfos($document){
// $document: سند HTML
//ما در جستجوی فهرست خطاها هستیم
// یا جدول شبیهسازی
//آمادهسازی نتیجه
$impots[erreurs]=array();
$impots[simulations]=array();
- به خاطر داشته باشید که خطاها توسط سرور در قالب پیام خطا ارسال میشوند. عبارت منظم "|<li>(.+?)</li>|" باید امکان بازیابی این اطلاعات را فراهم کند. در اینجا، ما از کاراکتر | برای جدا کردن عبارت منظم به جای کاراکتر / استفاده کردهایم، زیرا کاراکتر دوم همچنین در داخل خود عبارت منظم ظاهر میشود. توابع preg_match_all ($modèle, $document, $champs, PREG_SET_ORDER) تمام رخدادهای $modèle یافتشده در $document را بازیابی میکند. این موارد در آرایه $champs ذخیره میشوند. بنابراین، $champs[i] نمایانگر i-امین رخداد $modèle در $document است. در $champs[$i][0]، رشتهٔ متناظر با قالب قرار میگیرد. اگر قالب شامل پرانتز باشد، رشته متناظر با اولین پرانتز در $champs[$i][1] قرار داده میشود، دومی در $champs[$i][2] و به همین ترتیب قرار میگیرد. بنابراین کد بازیابی خطاها به شرح زیر خواهد بود:
<?php
...
//آیا خطایی وجود دارد؟
// قالب خطای خط
$modErreur="|<li>(.+?)</li>|";
//جستجو در سند
if(preg_match_all($modErreur,$document,$champs,PREG_SET_ORDER)){
//بازیابی خطاها
for($i=0;$i<count($champs);$i++){
$impots[erreurs][]=$champs[$i][1];
}//برای
//انجام شد
return $impots;
}//اگر
4.7. برنامه مالیات: نتیجهگیری
ما نسخههای مختلفی از برنامهٔ محاسبه مالیات مبتنی بر مدل مشتری-سرور خود را نشان دادهایم:
- نسخه ۱: سرویس توسط مجموعهای از برنامههای PHP ارائه میشود؛ کلاینت یک مرورگر وب است. این برنامه یک شبیهسازی انجام میدهد و هیچ سابقهای از شبیهسازیهای قبلی را نگه نمیدارد.
- نسخه ۲: ما با جاسازی اسکریپتهای جاوااسکریپت در سند HTML که توسط مرورگر بارگذاری میشود، قابلیتهای سمت مرورگر را اضافه میکنیم. این کار اعتبار پارامترهای فرم را بررسی میکند.
- نسخه ۳: سرویس قادر است با مدیریت یک جلسه (session)، شبیهسازیهای مختلف انجامشده توسط کلاینت را به خاطر بسپارد. رابط کاربری HTML نیز به همین ترتیب برای نمایش این شبیهسازیها اصلاح شده است.
- نسخه ۴: کلاینت اکنون یک برنامه کنسول مستقل است. این به ما امکان میدهد تا به توسعه کلاینتهای وب بازگردیم.
در این مرحله، چند نکته شایان ذکر است:
- نسخههای ۱ تا ۳ از مرورگرهایی پشتیبانی میکنند که قابلیت دیگری به جز اجرای اسکریپتهای جاوا اسکریپت ندارند. توجه باید داشت که کاربر همیشه این امکان را دارد که اجرای این اسکریپتها را غیرفعال کند. در این صورت، برنامه در نسخه ۱ تنها به صورت جزئی کار خواهد کرد (گزینه «پاک کردن» کار نخواهد کرد) و در نسخههای ۲ و ۳ اصلاً کار نخواهد کرد (گزینههای «پاک کردن» و «محاسبه» کار نخواهند کرد). شاید ارزش داشته باشد که نسخهای از سرویس را در نظر بگیریم که از اسکریپتهای جاوااسکریپت استفاده نکند.
هنگام نوشتن یک سرویس وب، باید در نظر گرفت که هدف کدام نوع از کلاینتها است. اگر هدف دسترسی به گستردهترین مخاطبان ممکن باشد، باید برنامهای نوشت که فقط HTML را به مرورگرها ارسال کند (بدون جاوا اسکریپت یا اپلتها). اگر در یک اینترانت کار میکنید و بر پیکربندی ایستگاههای کاری آن کنترل دارید، میتوانید از نظر الزامات کلاینت سختگیرانهتر عمل کنید.
نسخه ۴ یک کلاینت وب است که اطلاعات مورد نیاز خود را از استریم HTML ارسالشده توسط سرور بازیابی میکند. اغلب اوقات، ما هیچ کنترلی بر این استریم نداریم. این حالت زمانی است که ما یک کلاینت برای یک سرویس وب موجود در شبکه نوشتهایم که توسط شخص دیگری مدیریت میشود. بیایید یک مثال بزنیم. فرض کنید سرویس شبیهسازی محاسبه مالیات ما توسط شرکت X نوشته شده باشد. در حال حاضر، این سرویس شبیهسازیها را در یک جدول HTML ارسال میکند و کلاینت ما از این موضوع برای بازیابی آنها استفاده میکند. بنابراین، هر خط از پاسخ سرور را با عبارت منظم زیر مقایسه میکند:
// نمونهای از یک سطر در جدول شبیهسازی
// yes220000022504
$modSimulation="|<tr>\s*<td>(.+?)</td>\s*<td>(.+?)</td>\s*<td>(.+?)</td>\s*<td>(.+?)</td>\s*</tr>|";
حال فرض کنیم که توسعهدهندهٔ برنامه، ارائهٔ بصری پاسخ را با قرار دادن شبیهسازیها نه در یک جدول، بلکه در یک فهرست به شکل زیر تغییر میدهد:
در این حالت، کلاینت وب ما باید مجدداً نوشته شود. این تهدید دائمی است که کلاینتهای وب برای برنامههایی که خودمان کنترلشان را نداریم، با آن مواجه هستند. XML ممکن است راهحلی برای این مشکل ارائه دهد:
- به جای تولید HTML، سرویس شبیهسازی XML را تولید خواهد کرد. در مثال ما، این میتواند باشد
<simulations>
<entetes marie="marié" enfants="enfants" salaire="salaire" impot="impôt"/>
<simulation marie="oui" enfants="2" salaire="200000" impot="22504" />
<simulation marie="non" enfants="2" salaire="200000" impot="33388" />
</simulations>
- میتوانست یک صفحهآرایی (stylesheet) به این پاسخ مرتبط شود تا به مرورگرها دستور دهد چگونه آن را نمایش دهند XML
- کلاینتهای وب برنامهریزیشده این شیوهنامه را نادیده گرفته و اطلاعات را مستقیماً از جریان XML در پاسخ بازیابی میکنند
اگر طراح سرویس بخواهد ظاهر بصری نتایجی را که ارائه میدهد تغییر دهد، به جای XML، شیوهنامه را اصلاح خواهد کرد. به لطف شیوهنامه، مرورگرها طرحبندی بصری جدید را نمایش خواهند داد و کلاینتهای وب نیازی به اصلاح نخواهند داشت.
