5. XML و PHP
در این فصل، مقدمهای بر استفاده از اسناد XML (زبان نشانهگذاری eXtensible) با PHP ارائه خواهیم داد. این کار را در چارچوب برنامه مالیات مورد بحث در فصل قبلی انجام میدهیم.
5.1. فایلهای XML و صفحات سبک XSL
بیایید فایل زیر XML را در نظر بگیریم که میتواند نتایج شبیهسازیها را نشان دهد:
<?xml version="1.0" encoding="windows-1252"?>
<simulations>
<simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
<simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>
اگر آن را با استفاده از IE 6 مشاهده کنید، نتیجه زیر را دریافت میکنید:

IE6 تشخیص میدهد که با یک فایل XML (به لطف پسوند .xml فایل) سر و کار دارد و آن را به شیوهٔ خود قالببندی میکند. در نتاسکیپ، یک صفحه خالی نمایش داده میشود. با این حال، اگر کد منبع (View/Source) را مشاهده کنید، میتوانید فایل اصلی XML را ببینید:

چرا نتاسکیپ هیچ چیزی نمایش نمیدهد؟ زیرا به یک شیوهنامه نیاز دارد تا به آن بگوید چگونه فایل XML را به فایل HTML تبدیل کند، فایلی که سپس میتواند آن را نمایش دهد. مشخص شد که IE 6 دارای یک صفحهسبک پیشفرض است، در حالی که فایل XML چنین صفحهسبکی را ارائه نمیدهد، که در اینجا نیز همین وضعیت وجود داشت.
زبان XSL (زبان eXtensible StyleSheet) وجود دارد که امکان توصیف تبدیلات لازم برای تبدیل یک فایل XML به هر فایل متنی را فراهم میکند. XSL از دستورالعملهای متعددی پشتیبانی میکند و شباهت زیادی به زبانهای برنامهنویسی دارد. در اینجا وارد جزئیات نمیشویم، زیرا این موضوع چندین ده صفحه طول خواهد کشید. ما صرفاً دو مثال از صفحات سبک XSL را شرح خواهیم داد. اولین مثال، صفحهای است که فایل XML را به کد simulations.xml تبدیل میکند. ما مورد دوم را طوری تغییر میدهیم که شیوهنامهای را مشخص کند که مرورگرها میتوانند برای تبدیل آن به سند HTML قابل نمایش، از آن استفاده کنند:
<?xml version="1.0" encoding="windows-1252"?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>
<simulations>
<simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
<simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>
دستور XML
فایل simulations.xsl را بهعنوان یک xml-stylesheet از نوع text/xsl c.a.d تعیین میکند. یک فایل متنی حاوی کد XSL. این شیوهنامه توسط مرورگرها برای تبدیل متن XML به یک سند HTML استفاده خواهد شد. در اینجا نتیجه بهدستآمده با Netscape 7 هنگام بارگذاری فایل XML simulations.xml آمده است:

وقتی کد منبع سند را مشاهده میکنیم (View/Source)، به جای سند نمایش داده شده HTML، سند اصلی XML را میبینیم:

نتاسکیپ از صفحهآرایی simulations.xsl برای تبدیل سند XML بالا به سند قابل نمایش HTML استفاده کرد. اکنون زمان آن است که به محتویات این صفحهآرایی نگاهی بیندازیم:
<?xml version="1.0" encoding="windows-1252"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<title>Simulations de calculs d'impôts</title>
</head>
<body>
<center>
<h3>Simulations de calculs d'impôts</h3>
<hr/>
<table border="1">
<th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
<xsl:apply-templates select="/simulations/simulation"/>
</table>
</center>
</body>
</html>
</xsl:template>
<xsl:template match="simulation">
<tr>
<td><xsl:value-of select="@marie"/></td>
<td><xsl:value-of select="@enfants"/></td>
<td><xsl:value-of select="@salaire"/></td>
<td><xsl:value-of select="@impot"/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
- یک شیوهنامه با نام XSL یک فایل XML است و بنابراین از همان قوانین پیروی میکند. از جمله موارد دیگر، باید «خوبساخت» باشد، به این معنی که هر تگ آغازین باید بسته شود.
- این فایل با دو دستور، XML، آغاز میشود که میتوان آن را در هر صفحهشیوهای از نوع XSL در ویندوز گنجاند:
<?xml version="1.0" encoding="windows-1252"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
ویژگی encoding="windows-1252" امکان استفاده از کاراکترهای دارای نشانه (accented) را در شیوهنامه فراهم میکند.
- برچسب <xsl:output method="html" indent="yes"/> به تفسیرگر XSL میگوید که میخواهید خروجی «با حاشیه» HTML تولید کنید.
- برچسب <xsl:template match="element"> برای تعریف عنصر در سند XML استفاده میشود که دستورالعملهای موجود بین <xsl:template ...> و </xsl:template> بر روی آن اعمال خواهد شد.
در مثال بالا، عنصر "/" نشاندهنده ریشه سند است. این بدان معناست که به محض برخورد با شروع سند XML، دستورات XSL که بین این دو تگ قرار دارند، اجرا خواهند شد.
- هر چیزی که یک تگ XSL نباشد، بدون تغییر در جریان خروجی گنجانده میشود. تگهای XSL اجرا میشوند. برخی از آنها خروجیای را در جریان خروجی تولید میکنند. بیایید به مثال زیر نگاه کنیم:
<xsl:template match="/">
<html>
<head>
<title>Simulations de calculs d'impôts</title>
</head>
<body>
<center>
<h3>Simulations de calculs d'impôts</h3>
<hr/>
<table border="1">
<th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
<xsl:apply-templates select="/simulations/simulation"/>
</table>
</center>
</body>
</html>
</xsl:template>
شایان ذکر است که سند مورد تحلیل، XML، به شرح زیر است:
<?xml version="1.0" encoding="windows-1252"?>
<simulations>
<simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
<simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>
از ابتدای سند تحلیلشده XML (match="/")، تفسیرگر XSL متن را خروجی خواهد داد
<html>
<head>
<title>Simulations de calculs d'impôts</title>
</head>
<body>
<center>
<h3>Simulations de calculs d'impôts</h3>
<hr>
<table border="1">
<th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
توجه داشته باشید که در متن اصلی ما <hr/> داشتیم و نه <hr>. در متن اصلی، امکان نوشتن <hr> وجود نداشت، که اگرچه یک تگ معتبر HTML است، اما یک تگ نامعتبر XML میباشد. با این حال، ما در اینجا با متنی از نوع XML سروکار داریم که باید «خوبساخت» باشد، به این معنی که هر تگ باید بسته شود. بنابراین ما <hr/> را مینویسیم و از آنجا که <xsl:output text="html ..."> را نوشتهایم، مفسر متن <hr/> را به <hr> تبدیل خواهد کرد. پس از این متن، متنی که توسط فرمان XSL تولید شده است، خواهد آمد:
بعداً خواهیم دید که این متن چیست. در نهایت، تفسیرگر متن زیر را اضافه خواهد کرد:
دستور <xsl:apply-templates select="/simulations/simulation"/> به سیستم دستور میدهد تا قالب مربوط به عنصر /simulations/simulation را اجرا کند. این دستور هرگاه مفسر XSL در متن تجزیهشده XML با تگ <simulation>..</simulations> یا <simulation/> درون تگ <simulations>..</simulations> مواجه شود، اجرا خواهد شد. هنگام برخورد با چنین تگی، مفسر دستورالعملهای قالب زیر را اجرا خواهد کرد:
<xsl:template match="simulation">
<tr>
<td><xsl:value-of select="@marie"/></td>
<td><xsl:value-of select="@enfants"/></td>
<td><xsl:value-of select="@salaire"/></td>
<td><xsl:value-of select="@impot"/></td>
</tr>
</xsl:template>
بیایید خطوط زیر از XML را در نظر بگیریم:
خط <simulation ..> با قالب دستور XSL <xsl:apply-templates select="/simulations/simulation"> مطابقت دارد. بنابراین تفسیرگر XSL تلاش خواهد کرد تا دستورات مربوط به این قالب را بر روی آن اعمال کند. این دستورالعمل قالب <xsl:template match="simulation"> را پیدا کرده و آن را اجرا خواهد کرد. به یاد داشته باشید که هر چیزی که یک فرمان XSL نباشد، توسط مفسر XSL بدون تغییر عبور داده میشود، در حالی که فرمانهای XSL با نتیجه اجرای آنها جایگزین میشوند. دستور XSL <xsl:value-of select="@champ"/> بدین ترتیب با مقدار ویژگی «champ» گره تجزیهشده (در این مورد، یک گره <simulation>) جایگزین میشود. تجزیه خط قبلی XML خروجی زیر را تولید خواهد کرد:
XSL | خروجی |
<xsl:value-of select="@marie"/> | |
<xsl:value-of select="@marie"/> | بله |
<xsl:value-of select="@children"/> | ۲ |
<xsl:value-of select="@salary"/> | ۲۰۰۰۰۰ |
<xsl:value-of select="@tax"/> | ۲۲۵۰۴ |
<xsl:value-of select="@impot"/>22504<x |
در مجموع، خط XML
به خط HTML تبدیل خواهد شد:
تمام این توضیحات تا حدی ابتدایی هستند، اما اکنون برای خواننده روشن است که متن زیر XML:
<?xml version="1.0" encoding="windows-1252"?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>
<simulations>
<simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
<simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>
همراه با برگهٔ سبک زیر: XSL simulations.xsl:
<?xml version="1.0" encoding="windows-1252"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<title>Simulations de calculs d'impôts</title>
</head>
<body>
<center>
<h3>Simulations de calculs d'impôts</h3>
<hr/>
<table border="1">
<th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
<xsl:apply-templates select="/simulations/simulation"/>
</table>
</center>
</body>
</html>
</xsl:template>
<xsl:template match="simulation">
<tr>
<td><xsl:value-of select="@marie"/></td>
<td><xsl:value-of select="@enfants"/></td>
<td><xsl:value-of select="@salaire"/></td>
<td><xsl:value-of select="@impot"/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
متن زیر را تولید میکند: HTML:
<html>
<head>
<title>Simulations de calculs d'impôts</title>
</head>
<body>
<center>
<h3>Simulations de calculs d'impots</h3>
<hr>
<table border="1">
<th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
<tr>
<td>oui</td><td>2</td><td>200000</td><td>22504</td>
</tr>
<tr>
<td>non</td><td>2</td><td>200000</td><td>33388</td>
</tr>
</table>
</center>
</body>
</html>
فایل زیر: XML simulations.xml
<?xml version="1.0" encoding="windows-1252"?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>
<simulations>
<simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
<simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>
وقتی در یک مرورگر جدید (در این مورد، نتاسکیپ ۷) مشاهده شود، به شکل زیر نمایش داده میشود:

5.2. برنامه مالیات: نسخه ۵
5.2.1. فایلهای XML و صفحات سبک XSL برای برنامه مالیات
بیایید به نقطه شروع خود، یعنی وباپلیکیشن مالیاتی، بازگردیم و به یاد آوریم که میخواهیم آن را طوری تغییر دهیم که پاسخ ارسالشده به کلاینتها به جای HTML، در قالب XML باشد. این پاسخ HTML با یک صفحهشیوهنامه XSL همراه خواهد بود تا مرورگرها بتوانند آن را نمایش دهند. در پاراگراف قبلی، ما ارائه کردیم:
- فایل simulations.xml، که نمونه اولیه یک پاسخ XML حاوی محاسبات مالیاتی شبیهسازیشده است
- فایل simulations.xsl، که بهعنوان صفحهشیوه XSL همراه این پاسخ XML عمل خواهد کرد
ما همچنین باید برای حالتی که پاسخ حاوی خطا باشد، پیشبینی کنیم. در این حالت، نمونه اولیه پاسخ XML فایل زیر خواهد بود: erreurs.xml:
<?xml version="1.0" encoding="windows-1252"?>
<?xml-stylesheet type="text/xsl" href="erreurs.xsl"?>
<erreurs>
<erreur>erreur 1</erreur>
<erreur>erreur 2</erreur>
</erreurs>
صفحهٔ سبک erreurs.xsl که برای نمایش این سند XML در یک مرورگر استفاده میشود، به شرح زیر خواهد بود:
<?xml version="1.0" encoding="windows-1252"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<title>Simulations de calculs d'impôts</title>
</head>
<body>
<center>
<h3>Simulations de calculs d'impôts</h3>
</center>
<hr/>
Les erreurs suivantes se sont produites :
<ul>
<xsl:apply-templates select="/erreurs/erreur"/>
</ul>
</body>
</html>
</xsl:template>
<xsl:template match="erreur">
<li><xsl:value-of select="."/></li>
</xsl:template>
</xsl:stylesheet>
این شیوهنامه یک دستور XSL را معرفی میکند که هنوز ملاقات نشده است: <xsl:value-of select="."/>. این دستور مقدار گره تجزیهشده را خروجی میدهد، در این مورد یک گره <erreur>texte</erreur>. مقدار این گره، متن بین تگهای آغاز و پایان است، در این مورد texte.
کد erreurs.xml توسط شیوهنامه erreurs.xsl به سند زیر HTML تبدیل میشود:
<html>
<head>
<title>Simulations de calculs d'impots</title>
</head>
<body>
<center>
<h3>Simulations de calculs d'impots</h3>
</center>
<hr>
Les erreurs suivantes se sont produites :
<ul>
<li>erreur 1</li>
<li>erreur 2</li>
</ul>
</body>
</html>
فایل erreurs.xml، به همراه صفحهٔ سبک آن، توسط یک مرورگر وب به صورت زیر نمایش داده میشود:

5.2.2. برنامهٔ xmlsimulations
ما فایلی به نام xmlsimulations.html ایجاد میکنیم و آن را در دایرکتوری برنامهٔ impots قرار میدهیم. صفحهٔ نمایشدادهشده به شرح زیر است:

این سند HTML یک سند ایستا است. کد آن به شرح زیر است:
<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="";
}//با
}//پاک
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/7/images/standard.jpg">
<center>
Calcul d'impôts
<hr>
<form name="frmImpots" action="/poly/impots/7/xmlsimulations.php" method="POST">
<table>
<tr>
<td>Etes-vous marié(e)</td>
<td>
<input type="radio" name="optMarie" value="oui">oui
<input type="radio" name="optMarie" value="non" checked>non
</td>
</tr>
<tr>
<td>Nombre d'enfants</td>
<td><input type="text" size="3" name="txtEnfants" value=""></td>
</tr>
<tr>
<td>Salaire annuel</td>
<td><input type="text" size="10" name="txtSalaire" value=""></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>
</body>
</html>
شایان ذکر است که دادههای فرم به URL/poly/impots/7/xmlsimulations.php ارسال میشوند. کد مربوط به برنامه xmlsimulations.php بسیار شبیه کد مربوط به برنامه impots.php است. از خوانندگان دعوت میشود به آن مراجعه کنند. بهعنوان یادآوری، کد ورودی به شرح زیر است:
<?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);
...
صفحات HTML توسط خطوط «include» «...» نمایش داده شدند. در اینجا، ما میخواهیم به جای HTML، XML را تولید کنیم. تمام کاری که باید انجام دهیم نوشتن دو برنامه جدید impots_erreurs.php و impots_simulations.php است، به طوری که آنها به جای HTML، XML را تولید کنند. بقیهٔ برنامه بدون تغییر باقی میماند. کد سپس به صورت زیر درمیآید:
<?php
// فرم مالیات را پردازش میکند
//کتابخانهها
include "ImpotsDSN.php";
// شروع جلسه
session_start();
// پیکربندی برنامه
ini_set("register_globals","off");
ini_set("display_errors","off");
$formulaireImpots="xmlsimulations.html";
$erreursImpots="impots_erreurs.php";
$simulationsImpots="impots_simulations.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);
//نمایش صفحه خطا در قالب XML
header("Content-type: text/xml");
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])){
//نمایش فرم خالی
include $formulaireImpots;
//پایان
terminerSession($session);
}//اگر
//بررسی پارامترها
$requête=vérifier($requête);
// آیا خطایی وجود دارد؟
if(count($requête[erreurs])!=0){
//نمایش خطاها در قالب XML
header("Content-type: text/xml");
include "$erreursImpots";
//پایان
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];
//نمایش شبیهسازیها در قالب XML
header("Content-type: text/xml");
include "$simulationsImpots";
//پایان
terminerSession($session);
ما قبلاً دو نوع پاسخ XML را که باید ارائه شوند، و همچنین صفحات سبک (استایلشیتها) که باید همراه آنها باشند، ارائه و بررسی کردهایم. کد برنامه impots_simulations.php به شرح زیر است:
<?php
//تولید کد XML برای صفحه شبیهسازیهای برنامه مالیات
// برخی ثابتها
$xslSimulations="simulations.xsl";
// سربرگهای XML
echo "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
echo "<?xml-stylesheet type=\"text/xsl\" href=\"$xslSimulations\" ?>\n";
// شبیهسازیها
echo "<simulations>\n";
for ($i=0;$i<count($requête[simulations]);$i++){
//شبیهسازی $i
echo "<simulation marie=\"".$requête[simulations][$i][0]."\" ".
"enfants=\"".$requête[simulations][$i][1]."\" ".
"salaire=\"".$requête[simulations][$i][2]."\" ".
"impot=\"".$requête[simulations][$i][3]."\" />\n";
}//برای
echo "</simulations>\n";
?>
این کد امکان تولید کد XML را، مشابه موارد زیر، از فرهنگ لغت $requête فراهم میکند:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl" ?>
<simulations>
<simulation marie="non" enfants="3" salaire="200000" impot="22504" />
<simulation marie="oui" enfants="3" salaire="200000" impot="16400" />
<simulation marie="oui" enfants="2" salaire="200000" impot="22504" />
</simulations>
فایل سبک simulations.xsl این کد XML را به کد HTML تبدیل خواهد کرد.
کد برنامه impots_erreurs.php به شرح زیر است:
<?php
//کد XML را برای صفحهٔ خطای برنامهٔ impots تولید میکند
// برخی ثابتها
$xslErreurs="erreurs.xsl";
// سربرگها XML
echo "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
echo "<?xml-stylesheet type=\"text/xsl\" href=\"$xslErreurs\" ?>\n";
//خطاها
echo "<erreurs>\n";
for ($i=0;$i<count($requête[erreurs]);$i++){
//خطا $i
echo "<erreur>".$requête[erreurs][$i]."</erreur>";
}//برای
echo "</erreurs>\n";
?>
این کد امکان تولید کد XML را، مشابه موارد زیر، از فرهنگ لغت $requête فراهم میکند:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<?xml-stylesheet type="text/xsl" href="erreurs.xsl" ?>
<erreurs>
<erreur>Impossible d'ouvrir la base DSN [mysql-dbimpots] (S1000)</erreur>
</erreurs>
فایل سبک erreurs.xsl این کد XML را به کد HTML تبدیل خواهد کرد.
بیایید به اولین مثال نگاه کنیم:

SGBD MySQL فعال نمیشود. سپس پاسخ زیر را دریافت میکنیم:

اگر به کد منبع دریافتی توسط مرورگر نگاه کنیم، موارد زیر را مشاهده میکنیم:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<?xml-stylesheet type="text/xsl" href="erreurs.xsl" ?>
<erreurs>
<erreur>Impossible d'ouvrir la base DSN [mysql-dbimpots] (S1000)</erreur>
</erreurs>
اکنون، ما SGBD و MySQL را اجرا کرده و مجموعهای از شبیهسازیهای مختلف را انجام میدهیم. پاسخ زیر را دریافت میکنیم:

کدی که توسط مرورگر دریافت میشود به شرح زیر است:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl" ?>
<simulations>
<simulation marie="oui" enfants="2" salaire="200000" impot="22504" />
<simulation marie="non" enfants="2" salaire="200000" impot="33388" />
<simulation marie="non" enfants="3" salaire="200000" impot="22504" />
<simulation marie="oui" enfants="3" salaire="200000" impot="16400" />
</simulations>
شایان ذکر است که نگهداری برنامه جدید ما آسانتر از نسخه قبلی است. بخشی از کار به صفحات سبک XSL منتقل شده است. مزیت این تقسیم وظایف جدید در این است که پس از نهایی شدن قالب XML برای پاسخها، توسعه صفحات سبک به توسعه برنامه وابسته نخواهد بود.
5.3. تحلیل یک سند XML در PHP
نسخه بعدی برنامه مالیاتی ما یک برنامه کلاینت مبتنی بر برنامه قبلی، xmlsimulations، خواهد بود. بنابراین برنامه کلاینت ما کد XML را دریافت خواهد کرد که باید آن را تجزیه کند تا اطلاعات مورد نیاز خود را استخراج نماید. اکنون از نسخههای مختلف خود فاصله میگیریم تا بیاموزیم چگونه یک سند XML را به PHP تجزیه و تحلیل کنیم. این کار را با استفاده از مثال زیر انجام خواهیم داد:
dos>e:\php43\php.exe xmlParser.php
syntaxe : xmlParser.php fichierXML
اپلیکیشن xmlParser.php یک پارامتر را میپذیرد: URI (شناسگر یکپارچه منبع) سند XML که باید تحلیل شود. در مثال ما، این URI صرفاً نام یک فایل XML خواهد بود که در دایرکتوری برنامه xmlParser.php قرار دارد. بیایید دو مثال از اجرا را در نظر بگیریم. در مثال اول، فایل XML که در حال تحلیل است، فایل erreurs.xml زیر است:
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="erreurs.xsl"?>
<erreurs>
<erreur>erreur 1</erreur>
<erreur>erreur 2</erreur>
</erreurs>
تحلیل نتایج زیر را به دست میدهد:
dos>e:\php43\php.exe xmlParser.php erreurs.xml
ERREURS
ERREUR
[erreur 1]
/ERREUR
ERREUR
[erreur 2]
/ERREUR
/ERREURS
ما هنوز توضیح ندادهایم که برنامه xmlParser.php چه کاری انجام میدهد، اما در اینجا میتوانیم ببینیم که ساختار سند تحلیلشده XML را نمایش میدهد. مثال دوم فایلهای زیر XML و simulations.xml را تحلیل میکند:
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>
<simulations>
<simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
<simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>
تحلیل نتایج زیر را به دست میدهد:
dos>e:\php43\php.exe xmlParser.php simulations.xml
SIMULATIONS
SIMULATION,(MARIE,oui) (ENFANTS,2) (SALAIRE,200000) (IMPOT,22504)
/SIMULATION
SIMULATION,(MARIE,non) (ENFANTS,2) (SALAIRE,200000) (IMPOT,33388)
/SIMULATION
/SIMULATIONS
برنامه xmlParser.php شامل همه چیزهایی است که برای درخواست مالیاتی خود نیاز داریم، زیرا توانسته است هم خطاها و هم شبیهسازیهایی را که سرور وب ممکن است ارسال کند، مدیریت کند. بیایید نگاهی به کد آن بیندازیم:
<?php
// سینتکس $0 fichierXML
//ساختار و محتویات فایل fichierXML را نمایش میدهد
//تأیید تماس
if(count($argv)!=2){
// پیام خطا
fwrite(STDERR,"syntaxe : $argv[0] fichierXML\n");
//پایان
exit(1);
}//اگر
// ابتداییسازیها
$file=$argv[1]; // فایل XML
$depth=0; // سطح تورفتگی = عمق در ساختار درختی
// برنامه
//یک شیء تحلیل متن XML ایجاد میشود
$xml_parser=xml_parser_create();
//مشخص میکند که کدام توابع در شروع و پایان یک تگ اجرا شوند
xml_set_element_handler($xml_parser,"startElement","endElement");
//مشخص میکند کدام تابع هنگام برخورد با دادهها اجرا شود
xml_set_character_data_handler($xml_parser,"afficheData");
//فایل XML را در حالت خواندن باز کنید
if (! ($fp=@fopen($file,"r"))){
fwrite(STDERR,"impossible d'ouvrir le fichier xml $file\n");
exit(2);
}//اگر
// فایل XML را در بلوکهای ۴۰۹۶ بایتی پردازش میکند
while($data=fread($fp,4096)){
//دادههای خواندهشده را تحلیل میکند
if (! xml_parse($xml_parser,$data,feof($fp))){
// یک خطا رخ داده است
fprintf(STDERR,"erreur XML : %s à la ligne %d\n",
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser));
//پایان
exit(3);
}//اگر
}//در حالی که
// فایل پردازش شده است
//منابع مورد استفاده توسط پارسر XML در حال آزاد شدن هستند
xml_parser_free($xml_parser);
// پایان
exit(0);
// -----------------------------------------------------------
//تابع هنگام برخورد با تگ آغازین فراخوانی میشود
function startElement($parser,$name,$attributs){
global $depth;
// یک دنبالهٔ فاصله (فاصلهگذاری)
for($i=0;$i<$depth;$i++){
print " ";
}//برای
// ویژگیها
$précisions="";
while(list($attrib,$valeur)=each($attributs)){
$précisions.="($attrib,$valeur) ";
}
// نام تگ و هرگونه ویژگی نمایش داده میشوند
if($précisions)
print "$name,$précisions\n";
else print "$name\n";
// یک سطح اضافی در ساختار درختی
$depth++;
}//startElement
// -----------------------------------------------------------
// تابعی که هنگام برخورد با تگ پایانی فراخوانی میشود
function endElement($parser,$name){
// برچسب پایانی
// سطح تورفتگی
global $depth;
$depth--;
// یک دنباله از فاصلهها (فرورفتگی)
for($i=0;$i<$depth;$i++){
echo " ";
}//برای
// نام تگ
echo "/$name\n";
}//endElement
// -----------------------------------------------------------
// تابع نمایش داده
function afficheData($parser,$data){
// سطح تورفتگی
global $depth;
// دادهها نمایش داده میشوند
$data=trim($data);
if($data!=""){
// یک دنباله از فاصلهها (فاصلهٔ تورفتگی)
for($i=0;$i<$depth;$i++){
echo " ";
}//برای
echo "[$data]\n";
}//اگر
}//afficheData
?>
بیایید کد مربوط به XML را بررسی کنیم. برای تحلیل یک سند XML، برنامه ما به یک تحلیلگر کد XML نیاز دارد که معمولاً «پارسر» نامیده میشود.
هنگامی که پارسر سند XML را تحلیل میکند، رویدادهایی مانند: شروع سند، شروع یک تگ، یک ویژگی تگ، محتوای یک تگ، پایان یک تگ، پایان سند و غیره تولید میکند. این رویدادها را به متدهایی که باید مشخص کنیم ارسال میکند:
<?php
...
//مشخص میکند که کدام توابع در شروع و پایان یک تگ اجرا شوند
xml_set_element_handler($xml_parser,"startElement","endElement");
// تعیین میکند که هنگام برخورد با دادهها کدام تابع اجرا شود
xml_set_character_data_handler($xml_parser,"afficheData");
رویدادی که توسط ... منتشر میشود پارسر | روش پردازش |
تابع startElement($parser, $name, $attributes) $parser: پارسر سند $name: نام عنصر تجزیهشده. اگر عنصر برخورد شده <simulations> باشد، مقدار name="simulations" خواهد بود. $attributs: فهرست ویژگیهای تگ به شکل (ATTRIBUT,value)، که در آن ATTRIBUT نام تگ به صورت حروف بزرگ است. | |
تابع displayData($parser, $data) $parser: پارسر سند $data: دادههای تگ | |
تابع پایان عنصر ($پارسر، $نام) پارامترها متعلق به متد startElement هستند. |
تابع startElement به شما امکان میدهد با استفاده از پارامتر $attributs ویژگیهای عنصر را بازیابی کنید. این یک دیکشنری از ویژگیهای تگ است. بنابراین، اگر تگ زیر را داشته باشیم:
واژهنامه $attributs به شکل زیر خواهد بود: array(wife=>yes,children=>2,salary=>200000,tax=>22504)
پس از تعریف پارسر و متدهای پیشین، یک سند با استفاده از تابع xml_parse تحلیل میشود:
<?php
...
// پردازش فایل XML در بلوکهای ۴۰۹۶ بایتی
while($data=fread($fp,4096)){
// تحلیل دادههای خواندهشده
if (! xml_parse($xml_parser,$data,feof($fp))){
...
}//اگر
}//در حالی که
تابع xml_parse($parser, $doc, $end) پارسر $parser سند $doc را تحلیل میکند. $doc ممکن است بخشی از یک سند بزرگتر باشد. پارامتر $fin نشان میدهد که آیا این بخش آخر است (true) یا خیر (false). هنگام تجزیهٔ سند $doc، توابعی که توسط xml_set_element_handler تعریف شدهاند در ابتدای و انتهای هر تگ فراخوانی میشوند. آیا تابع تعریفشده توسط xml_set_character_data_handler هر بار که محتوای یک تگ بازیابی میشود، فراخوانی میشود؟ |
هنگام تجزیه و تحلیل سند XML، ممکن است خطاهایی رخ دهد، بهویژه اگر سند XML «بدشکل» باشد، برای مثال در مواردی که تگهای پایانی وجود ندارند. در این حالت، تابع xml_parse مقداری را برمیگرداند که به عنوان «false» ارزیابی میشود:
<?php
...
if (! xml_parse($xml_parser,$data,feof($fp))){
// یک خطا رخ داده است
fprintf(STDERR,"erreur XML : %s à la ligne %d\n",
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser));
//پایان
exit(3);
}//اگر
تابع xml_get_error_code($parser) کد آخرین خطای رخداده را برمیگرداند -- تابع xml_error_string($code) پیام خطا را که با کد ارسالشده بهعنوان پارامتر مرتبط است، بازمیگرداند -- تابع xml_get_current_line($parser) شماره خط سند XML را که در حال تجزیه است بازمیگرداند |
پس از تجزیه سند، منابع تخصیصیافته به تجزیهگر آزاد میشوند:
function xml_free($parser) |
اکنون که این موضوع توضیح داده شده است، برنامه قبلی به همراه خروجیهای نمونه، خودگویا است.
5.4. برنامه مالیات: نسخه ۶
اکنون تمام عناصر لازم برای نوشتن یک برنامهٔ کلاینت برای سرویس مالیاتی ما که خروجی XML را تولید میکند، در اختیار داریم. ما از نسخهٔ ۴ برنامهمان برای ساخت کلاینت استفاده میکنیم و نسخهٔ ۵ را برای سرور حفظ میکنیم. در این برنامهٔ کلاینت-سرور:
- خدمات شبیهسازی محاسبه مالیات توسط برنامه xmlsimulations.php ارائه میشود. بنابراین پاسخ سرور در قالب XML است، همانطور که در نسخه 5 دیدیم.
- کلاینت دیگر یک مرورگر نیست، بلکه یک کلاینت مستقل PHP است. رابط کنسول آن مربوط به نسخه ۴ است.
از خوانندگان دعوت میشود تا کد برنامه cltImpots.php را که برنامه کلاینت برای نسخه ۴ بود، بررسی کنند. این برنامه یک سند $document را از سرور دریافت کرد. آن سند نیز به نوبه خود یک سند HTML بود. اکنون یک سند XML است. اسناد HTML و $document توسط تابع زیر تحلیل شدند:
<?php
...
function getInfos($document){
// $document: سند HTML
// ما به دنبال یا فهرست خطاها هستیم
//یا جدول شبیهسازی
//آمادهسازی نتیجه
$impots[erreurs]=array();
$impots[simulations]=array();
........
return $impots;
}//getInfos
تابع سند HTML $document را دریافت کرد، آن را تحلیل نمود و یک دیکشنری $impots با دو ویژگی بازگرداند:
- errors: یک آرایه از خطاها
- شبیهسازیها: آرایهای از شبیهسازیها که هر شبیهسازی خود آرایهای با چهار عنصر (همسر، فرزندان، حقوق، مالیات) است.
اپلیکیشن cltImpots.php اکنون به cltXmlSimulations.php تغییر نام داده شده است. تنها بخشی که سند دریافتشده از سرور را پردازش میکند، باید تغییر یابد تا این واقعیت را در نظر بگیرد که اکنون یک سند XML است. تابع getInfos سپس به صورت زیر در میآید:
<?php
...
// --------------------------------------------------------------
function getInfos($document){
// $document: سند XML
//ما در جستجوی فهرست خطاها هستیم
// یا جدول شبیهسازی
global $impots,$balises;
//آمادهسازی نتیجه
$impots[erreurs]=array();
$impots[simulations]=array();
// برچسبها در حال انجام
$balises=array();
// ایجاد یک شیء تحلیل متن XML
$xml_parser=xml_parser_create();
//مشخص میکند کدام توابع را در شروع و پایان تگ اجرا کند
xml_set_element_handler($xml_parser,"startElement","endElement");
//مشخص کردن تابع قابل اجرا هنگام برخورد با دادهها
xml_set_character_data_handler($xml_parser,"getData");
// پارس کردن $document
xml_parse($xml_parser,$document);
//منابع اشغالشده توسط پارسر XML را آزاد میکند
xml_parser_free($xml_parser);
//پایان
return $impots;
}//getInfos
// -----------------------------------------------------------
// تابعی که هنگام برخورد با یک تگ آغازین فراخوانی میشود
function startElement($parser,$name,$attributs){
global $impots,$balise,$balises,$contenu;
//نام تگ و محتوای آن ثبت میشود
$balise=strtolower($name);
$contenu="";
// به پشته تگ اضافه میشود
array_push($balises,$balise);
//آیا این یک شبیهسازی است؟
if($balise=="simulation"){
// ویژگیهای شبیهسازی را ثبت کنید
$impots[simulations][]=array($attributs[MARIE],$attributs[ENFANTS],$attributs[SALAIRE],$attributs[IMPOT]);
}//اگر
}//startElement
// -----------------------------------------------------------
// تابعی که هنگام برخورد با تگ پایانی فراخوانی میشود
function endElement($parser,$name){
//بازیابی برچسب فعلی
global $impots,$balises,$contenu;
$balise=array_pop($balises);
//آیا این یک تگ خطا است؟
if($balise=="erreur"){
// یک خطای دیگر
$impots[erreurs][]=trim($contenu);
}//اگر
}//endElement
// -----------------------------------------------------------
// تابع پردازش محتوای یک تگ
function getData($parser,$data){
// دادههای جهانی
global $balise,$contenu;
//آیا این یک تگ خطا است؟
if($balise=="erreur"){
// افزودن به محتوای تگ فعلی
$contenu.=$data;
}//اگر
}//getData
نظرات:
- توابع getInfos($document) با ایجاد یک پارسر آغاز میشود، سپس آن را پیکربندی کرده و در نهایت شروع به تجزیه و تحلیل سند $document میکند:
<?php
...
//یک شیء تحلیل متن XML ایجاد میکند
$xml_parser=xml_parser_create();
//مشخص میکند که کدام توابع در شروع و پایان تگ اجرا شوند
xml_set_element_handler($xml_parser,"startElement","endElement");
//مشخص میکند کدام تابع هنگام برخورد با دادهها اجرا شود
xml_set_character_data_handler($xml_parser,"getData");
// پارس $document
xml_parse($xml_parser,$document);
- پس از اتمام تحلیل، منابع تخصیصیافته به تجزیهگر آزاد میشوند و فرهنگ لغت $impots بازگردانده میشود.
<?php
...
//منابع اشغالشده توسط پارسر XML را آزاد میکند
xml_parser_free($xml_parser);
//پایان
return $impots;
- تابع startElement($parser,$name,$attributs) در ابتدای هر تگ فراخوانی میشود. آن
- برچسب $name را در جدول برچسبهای $balises ثبت میکند. این جدول بهصورت یک پشته مدیریت میشود: با برخورد با نماد پایان برچسب، آخرین برچسب قرارگرفته در $balises از پشته خارج میشود. برچسب فعلی نیز در $balise ثبت میشود. فرهنگ لغت $attributs شامل ویژگیهای برچسب مواجههشده است، که این ویژگیها به صورت حروف بزرگ ثبت شدهاند.
- ویژگیها را در فرهنگ لغت جهانی $impots[simulations] ذخیره میکند، اگر یک تگ شبیهسازی باشد.
- توابع getData($parser,$data) زمانی که محتوای $data یک تگ بازیابی شده باشد. در اینجا اقدامی احتیاطی صورت گرفته است. در برخی کتابخانههای پردازش سند API، بهویژه جاوا، ذکر شده است که این تابع میتواند بارها فراخوانی شود، c.a.d. که محتوای یک تگ لزوماً بهصورت یکجا در دسترس نیست. در اینجا، مستندات به این محدودیت اشاره نمیکند. به عنوان یک اقدام احتیاطی، مقدار به دست آمده در یک متغیر سراسری ذخیره میشود. فقط با برخورد با نماد پایان تگ است که میتوانیم مطمئن شویم کل محتوای تگ را دریافت کردهایم. تنها تگی که از این پردازش تأثیر میپذیرد، تگ <erreur> است.
- تابع endElement($parser,$name) در انتهای هر تگ فراخوانی میشود. این تابع در اینجا برای تغییر نام تگ فعلی با حذف آخرین تگ از پشته تگها و افزودن محتویات تگ <erreur>، که در آرایه $impots[erreurs] پایان مییابد، استفاده میشود.
در اینجا چند مثال از نحوه کار آن آورده شده است، با شروع از یک SGBD که هنوز اجرا نشده است:
dos>e:\php43\php.exe cltXmlSimulations.php http://localhost/poly/impots/8/xmlsimulations.php بله 2 200000
Jeton de session=[e8c29ea12f79e4771960068d161229fd]
Les erreurs suivantes se sont produites :
Impossible d'ouvrir la base DSN [mysql-dbimpots] (S1000)
سپس با SGBD، که اجرا شد:
dos>e:\php43\php.exe cltXmlSimulations.php http://localhost/poly/impots/8/xmlsimulations.php yes 3 200000
Jeton de session=[69a54d79db10b70ed0a2d55d5026ac8b]
Simulations :
[oui,3,200000,16400]
dos >e:\php43\php.exe cltXmlSimulations.php http://localhost/poly/impots/8/xmlsimulations.php yes 2 200000 69a54d79db10b70ed0a2d55d5026ac8b
Jeton de session=[69a54d79db10b70ed0a2d55d5026ac8b]
Simulations :
[oui,3,200000,16400]
[oui,2,200000,22504]
dos >e:\php43\php.exe cltXmlSimulations.php http://localhost/poly/impots/8/xmlsimulations.php no 2 200000 69a54d79db10b70ed0a2d55d5026ac8b
Jeton de session=[69a54d79db10b70ed0a2d55d5026ac8b]
Simulations :
[oui,3,200000,16400]
[oui,2,200000,22504]
[non,2,200000,33388]
5.5. نتیجهگیری
به لطف پاسخ XML، مدیریت برنامه مالیاتی هم برای توسعهدهنده آن و هم برای توسعهدهندگان برنامههای کلاینت آسانتر شده است.
- اکنون طراحی برنامه سمت سرور را میتوان به دو نوع افراد سپرد: توسعهدهنده سرولت PHP و طراح گرافیکی که ظاهر پاسخ سرور را در مرورگرهای وب مدیریت خواهد کرد. فرد دوم صرفاً باید با ساختار پاسخ سرور آشنا باشد تا بتواند صفحات سبک همراه را ایجاد کند. شایان ذکر است که این موارد در فایلهای جداگانه XSL، مستقل از برنامه PHP، قرار دارند. بنابراین طراح گرافیک میتواند مستقل از توسعهدهنده کار کند.
- طراحان برنامههای کلاینت نیز به سادگی باید با ساختار پاسخ XML سرور آشنا باشند. هرگونه تغییری که طراح گرافیک ممکن است در صفحات سبک ایجاد کند، هیچ تأثیری بر این پاسخ XML ندارد، که همواره یکسان باقی میماند. این یک مزیت بزرگ است.
- توسعهدهنده چگونه میتواند اپلیکیشن PHP خود را بدون خراب کردن هیچ چیزی بهروزرسانی کند؟ اولاً، تا زمانی که پاسخ XML بدون تغییر باقی بماند، توسعهدهنده میتواند برنامه خود را هر طور که بخواهد سازماندهی کند. آنها همچنین میتوانند پاسخ XML را بهروزرسانی کنند، به شرطی که عناصر <error> و <simulation> مورد انتظار کلاینتهای خود را حفظ کنند. بنابراین میتوانند تگهای جدیدی به این پاسخ اضافه کنند. توسعهدهنده فرانتاند این موارد را در شیوهنامههای خود در نظر خواهد گرفت و مرورگرها قادر به نمایش نسخههای جدید پاسخ خواهند بود. با این حال، کلاینتهای کنترلشده از طریق برنامه همچنان با استفاده از قالب قدیمی به کار خود ادامه خواهند داد و تگهای جدید به سادگی نادیده گرفته میشوند. برای امکانپذیر شدن این کار، تگهای مورد جستجو باید در تحلیل پاسخ سرور در XML به درستی شناسایی شوند. این کاری است که در کلاینت XML ما برای اپلیکیشن مالیاتی انجام شد، جایی که رویهها صراحتاً بیان میکردند که ما در حال پردازش تگهای <erreur> و <simulation> هستیم. در نتیجه، سایر تگها نادیده گرفته میشوند.