5. XML ve PHP
Bu bölümde, XML (eXtensible İşaretleme Dili) belgelerinin PHP ile kullanımına giriş yapacağız. Bunu, bir önceki bölümde incelenen vergi uygulaması bağlamında gerçekleştireceğiz.
5.1. XML dosyaları ve XSL stil sayfaları
Simülasyon sonuçlarını temsil edebilecek aşağıdaki XML dosyasını ele alalım:
<?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>
Bunu IE 6 ile görüntülediğimizde şu sonuç elde edilir:

IE6, (dosyanın .xml uzantısı sayesinde) bir XML dosyasıyla karşı karşıya olduğunu algılar ve dosyayı kendine özgü bir şekilde sayfa düzenine yerleştirir. Netscape ile boş bir sayfa görüntülenir. Ancak kaynak koduna (Görünüm/Kaynak) bakıldığında, orijinal XML dosyası olduğu görülür:

Netscape neden hiçbir şey göstermiyor? Çünkü XML dosyasını, görüntüleyebileceği HTML dosyasına nasıl dönüştüreceğini belirten bir stil sayfasına ihtiyacı var. Aslında IE 6'nın varsayılan bir stil sayfası varken, XML dosyasında böyle bir stil sayfası bulunmuyor; bu durum burada da geçerliydi.
XSL (eXtensible StyleSheet Dili) adlı bir dil mevcuttur; bu dil, bir XML dosyasını herhangi bir metin dosyasına dönüştürmek için gerçekleştirilmesi gereken dönüşümleri tanımlamaya olanak tanır. XSL, çok sayıda komutun kullanımına izin verir ve programlama dillerine oldukça benzer. Burada ayrıntılı olarak ele almayacağız, çünkü bu onlarca sayfa gerektirecektir. Sadece iki XSL stil sayfası örneğini açıklayacağız. İlki, XML dosyasını simulations.xml koduna dönüştürecek olan stil sayfasıdır. Bu dosyayı, tarayıcıların onu görüntüleyebilecekleri HTML belgesine dönüştürmek için kullanabilecekleri stil sayfasını belirtecek şekilde değiştiriyoruz:
<?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 komutu
komutu, simulations.xsl dosyasını text/xsl türünde bir stil sayfası (xml-stylesheet) olarak tanımlar; c.a.d ise XSL kodunu içeren bir metin dosyasıdır. Bu stil sayfası, tarayıcılar tarafından XML metnini HTML belgesine dönüştürmek için kullanılacaktır. XML simulations.xml dosyasını yüklediğimizde Netscape 7 ile elde edilen sonuç şöyledir:

Belgenin kaynak koduna baktığımızda (Görünüm/Kaynak), görüntülenen HTML belgesi yerine, orijinal XML belgesini görüyoruz:

Netscape, yukarıdaki XML belgesini görüntülenebilir HTML belgesine dönüştürmek için simulations.xsl stil sayfasını kullanmıştır. Şimdi bu stil sayfasının içeriğine bir göz atalım:
<?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 stil sayfası, bir XML dosyasıdır ve bu nedenle onun kurallarına uyar. Diğer şeylerin yanı sıra "düzgün biçimlendirilmiş" olmalıdır, yani açılan her etiket kapatılmalıdır.
- Dosya, Windows'ta herhangi bir XSL stil sayfasında saklanabilecek iki komutla başlar:
<?xml version="1.0" encoding="windows-1252"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
encoding="windows-1252" özniteliği, stil sayfasında aksanlı karakterlerin kullanılmasını sağlar.
- <xsl:output method="html" indent="yes"/> etiketi, XSL yorumlayıcısına "girintili" HTML çıktısı üretilmesini istediğimizi belirtir.
- <xsl:template match="eleman"> etiketi, <xsl:template ...> ile </xsl:template> arasında yer alan talimatların uygulanacağı XML belgesindeki elemanı tanımlamak için kullanılır.
Yukarıdaki örnekte "/" öğesi, belgenin kökünü belirtir. Bu, XML belgesinin başlangıcına ulaşılır ulaşılmaz, iki etiket arasında yer alan XSL komutlarının yürütüleceği anlamına gelir.
- XSL etiketleri dışında kalan her şey, olduğu gibi çıktı akışına eklenir. XSL etiketleri ise yürütülür. Bunlardan bazıları çıktı akışında bir sonuç üretir. Aşağıdaki örneği inceleyelim:
<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>
Analiz edilen XML belgesinin içeriği şöyledir:
<?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>
Analiz edilen XML belgesinin başından itibaren (match="/"), XSL yorumlayıcısı şu metni çıktı olarak üretecektir
<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>
İlk metinde <hr/> yazılmıştı, <hr> değil. İlk metinde <hr> yazılmamıştı; bu, geçerli bir HTML etiketi olsa da, geçersiz bir XML etiketidir. Oysa burada "düzgün biçimlendirilmiş" olması gereken bir metinle karşı karşıyayız; yani her etiket kapatılmalıdır. Bu nedenle <hr/> yazıyoruz ve <xsl:output text="html ..."> yazdığımız için yorumlayıcı, <hr/> metnini <hr> olarak dönüştürecektir. Bu metnin ardından, XSL komutu tarafından üretilen metin gelecektir:
Bu metnin ne olduğunu daha sonra göreceğiz. Son olarak yorumlayıcı şu metni ekleyecektir:
<xsl:apply-templates select="/simulations/simulation"/> komutu, /simulations/simulation öğesinin "şablonunun" (template) çalıştırılmasını gerektirir. Bu komut, XSL yorumlayıcısı, analiz edilen XML metninde bir <simulations>..</simulations> etiketi içinde bir <simulation>..</simulations> veya <simulation/> etiketi ile her karşılaştığında çalıştırılacaktır. Böyle bir etiketle karşılaştığında, yorumlayıcı aşağıdaki şablondaki talimatları uygulayacaktır:
<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>
Şu XML satırlarını ele alalım:
<simulation ..> satırı, XSL <xsl:apply-templates select="/simulations/simulation>" komutunun şablonuna karşılık gelir. Dolayısıyla XSL yorumlayıcısı, bu şablona karşılık gelen komutları ona uygulamaya çalışacaktır. <xsl:template match="simulation"> şablonunu bulacak ve bunu çalıştıracaktır. Hatırlanacağı üzere, XSL komutu olmayan kısımlar XSL yorumlayıcısı tarafından olduğu gibi alınır ve XSL komutları ise yürütülmelerinin sonucu ile değiştirilir. Böylece XSL <xsl:value-of select="@champ"/> komutu, analiz edilen düğümün (burada bir <simulation> düğümü) "champ" özniteliğinin değeriyle değiştirilir. Bir önceki XML satırının analizi, aşağıdaki çıktıyı üretecektir:
XSL | çıktı |
<tr><td> | <tr><td> |
<xsl:value-of select="@marie"/> | evet |
</td><td> | </td><td> |
<xsl:value-of select="@enfants"/> | 2 |
</td><td> | </td><td> |
<xsl:value-of select="@salaire"/> | 200000 |
</td><td> | </td><td> |
<xsl:value-of select="@impot"/> | 22504 |
</td></tr> | </td></tr> |
Toplamda, XML satırı
satırı, HTML satırına dönüştürülecektir:
Tüm bu açıklamalar biraz basit olsa da, okuyucuya şu anda aşağıdaki XML metninin:
<?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>
aşağıdaki XSL simulations.xsl stil sayfası ile birlikte:
<?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>
Aşağıdaki HTML metnini üretir:
<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 dosyası simulations.xml dosyası
<?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>
güncel bir tarayıcıyla (burada Netscape 7) okunduğunda şu şekilde görüntülenir:

5.2. Vergi uygulaması: sürüm 5
5.2.1. Vergi uygulamasının XML dosyaları ve XSL stil sayfaları
Vergi web uygulamasına geri dönelim ve müşterilere verilen yanıtın HTML formatında değil, XML formatında olması için uygulamayı değiştirmek istediğimizi hatırlayalım. Bu HTML yanıtına, tarayıcıların onu görüntüleyebilmesi için bir XSL stil sayfası eşlik edecektir. Bir önceki paragrafta şunları sunmuştuk:
- simulations.xml dosyası; bu, vergi hesaplama simülasyonları içeren bir XML yanıtının prototipidir
- simulations.xsl dosyası; bu, XML yanıtına eşlik edecek XSL stil sayfası olacaktır
Ayrıca hatalı yanıt durumunu da göz önünde bulundurmamız gerekiyor. Bu durumda XML yanıtının prototipi, aşağıdaki erreurs.xml dosyası olacaktır:
<?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>
Bu erreurs.xsl belgesini bir tarayıcıda görüntülemek için kullanılan stil sayfası XML şu şekilde olacaktır:
<?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>
Bu stil sayfası, henüz karşılaşılmamış bir XSL komutunu tanıtmaktadır: <xsl:value-of select="."/>. Bu komut, analiz edilen düğümün değerini çıktı olarak verir; burada bu, bir <erreur>texte</erreur> düğümüdür. Bu düğümün değeri, açılış ve kapanış etiketleri arasında yer alan metindir; bu örnekte texte.
erreurs.xml kodu, erreurs.xsl stil sayfası tarafından aşağıdaki HTML belgesine dönüştürülür:
<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 dosyası ve stil sayfası, bir tarayıcıda şu şekilde görüntülenir:

5.2.2. xmlsimulations uygulaması
xmlsimulations.html adlı bir dosya oluşturup bunu impots uygulamasının dizinine yerleştiriyoruz. Görüntülenen sayfa şöyledir:

Bu HTML belgesi statik bir belgedir. Kodu şu şekildedir:
<html>
<head>
<title>impots</title>
<script language="JavaScript" type="text/javascript">
function effacer(){
// formun sıfırlanması
with(document.frmImpots){
optMarie[0].checked=false;
optMarie[1].checked=true;
txtEnfants.value="";
txtSalaire.value="";
txtImpots.value="";
}//with
}//sil
function calculer(){
// parametreleri sunucuya göndermeden önce doğrulama
with(document.frmImpots){
//çocuk sayısı
champs=/^\s*(\d+)\s*$/.exec(txtEnfants.value);
if(champs==null){
// şablon doğrulanmadı
alert("Le nombre d'enfants n'a pas été donné ou est incorrect");
nbEnfants.focus();
return;
}//eğer
//maaş
champs=/^\s*(\d+)\s*$/.exec(txtSalaire.value);
if(champs==null){
// şablon doğrulanmadı
alert("Le salaire n'a pas été donné ou est incorrect");
salaire.focus();
return;
}//eğer
// tamam - gönderiliyor
submit();
}//ile
}//hesapla
</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>
Formdaki verilerin URL /poly/impots/7/xmlsimulations.php adresine gönderildiğine dikkat edilmelidir. xmlsimulations.php uygulamasının kodu, impots.php uygulamasının koduna çok benzemektedir. Okuyucunun bu kodu tekrar gözden geçirmesi önerilir. Giriş kodunu hatırlatmak gerekirse:
<?php
// vergi formunu işliyor
// kütüphaneler
include "ImpotsDSN.php";
// oturum başlatma
session_start();
// uygulama yapılandırması
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);
// oturum parametrelerini alıyoruz
$session=$_SESSION["session"];
// oturum geçerli mi?
if(! isset($session) || ! isset($session[objImpots]) || ! isset($session[simulations])){
// yeni oturum başlatılıyor
$session=array(objImpots=>new ImpotsDSN($bdImpots),simulations=>array());
// Hata var mı?
if(count($session[objImpots]->erreurs)!=0){
$requête=array(erreurs=>$session[objImpots]->erreurs);
// hata sayfası görüntüleniyor
include $erreursImpots;
// son
$session=array();
terminerSession($session);
}//if
}//if
// devam eden işlemin parametrelerini alıyoruz
$requête[marié]=$_POST["optMarie"];
$requête[enfants]=$_POST["txtEnfants"];
$requête[salaire]=$_POST["txtSalaire"];
// tüm parametreler mevcut mu?
if(! isset($requête[marié]) || ! isset($requête[enfants]) || ! isset($requête[salaire])){
// boş form görüntüleniyor
$requête=array(chkoui=>"",chknon=>"checked",enfants=>"",salaire=>"",impots=>"",
erreurs=>array(),simulations=>array());
include $formulaireImpots;
// son
terminerSession($session);
}//if
// parametreleri kontrol et
$requête=vérifier($requête);
// Hata var mı?
if(count($requête[erreurs])!=0){
// form görüntüleme
include "$formulaireImpots";
// son
terminerSession($session);
}//eğer
// ödenecek vergi hesaplaması
$requête[impots]=$session[objImpots]->calculer(array(marié=>$requête[marié],
enfants=>$requête[enfants],salaire=>$requête[salaire]));
// bir simülasyon daha
$session[simulations][]=array($requête[marié],$requête[enfants],$requête[salaire],$requête[impots]);
$requête[simulations]=$session[simulations];
// formun görüntülenmesi
include "$formulaireImpots";
// son
terminerSession($session);
...
HTML sayfaları "... " include satırlarıyla görüntüleniyordu. Burada XML oluşturulmasını istiyoruz, HTML değil. HTML yerine XML'i oluşturması için impots_erreurs.php ve impots_simulations.php adlı iki yeni uygulama yazmak yeterlidir. Uygulamanın geri kalanında herhangi bir değişiklik yapılmaz. Kod şu şekilde olur:
<?php
// vergi formunu işler
// kütüphaneler
include "ImpotsDSN.php";
// oturum başlatma
session_start();
// uygulama yapılandırması
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);
// oturum ayarlarını alıyoruz
$session=$_SESSION["session"];
// oturum geçerli mi?
if(! isset($session) || ! isset($session[objImpots]) || ! isset($session[simulations])){
// yeni oturum başlatılıyor
$session=array(objImpots=>new ImpotsDSN($bdImpots),simulations=>array());
// Hata var mı?
if(count($session[objImpots]->erreurs)!=0){
$requête=array(erreurs=>$session[objImpots]->erreurs);
// hata sayfasını XML biçiminde görüntüleme
header("Content-type: text/xml");
include $erreursImpots;
// son
$session=array();
terminerSession($session);
}//if
}//if
// devam eden işlemin parametreleri alınır
$requête[marié]=$_POST["optMarie"];
$requête[enfants]=$_POST["txtEnfants"];
$requête[salaire]=$_POST["txtSalaire"];
// tüm parametreler mevcut mu?
if(! isset($requête[marié]) || ! isset($requête[enfants]) || ! isset($requête[salaire])){
// boş form görüntülenir
include $formulaireImpots;
// son
terminerSession($session);
}//if
// parametreleri kontrol et
$requête=vérifier($requête);
// Hata var mı?
if(count($requête[erreurs])!=0){
// hataları XML biçiminde görüntüleme
header("Content-type: text/xml");
include "$erreursImpots";
// son
terminerSession($session);
}//eğer
// ödenecek verginin hesaplanması
$requête[impots]=$session[objImpots]->calculer(array(marié=>$requête[marié],
enfants=>$requête[enfants],salaire=>$requête[salaire]));
// bir simülasyon daha
$session[simulations][]=array($requête[marié],$requête[enfants],$requête[salaire],$requête[impots]);
$requête[simulations]=$session[simulations];
// simülasyonların XML formatında görüntülenmesi
header("Content-type: text/xml");
include "$simulationsImpots";
// son
terminerSession($session);
Daha önce, sağlanması gereken iki tür yanıtı (XML) ve bunlara eşlik etmesi gereken stil sayfalarını tanıtmış ve incelemiştik. impots_simulations.php uygulamasının kodu şöyledir:
<?php
// vergi uygulamasının simülasyon sayfasının kodunu oluşturur XML
// bazı sabitler
$xslSimulations="simulations.xsl";
// XML başlıkları
echo "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
echo "<?xml-stylesheet type=\"text/xsl\" href=\"$xslSimulations\" ?>\n";
// simülasyonlar
echo "<simulations>\n";
for ($i=0;$i<count($requête[simulations]);$i++){
// simülasyon $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";
}//for
echo "</simulations>\n";
?>
Bu kod, $requête sözlüğünden, aşağıdakine benzer bir XML kodu oluşturulmasına olanak tanır:
<?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 stil sayfası, bu XML kodunu HTML koduna dönüştürecektir.
impots_erreurs.php uygulamasının kodu şöyledir:
<?php
// XML kodunu oluşturur, impots uygulamasının hata sayfası
// bazı sabitler
$xslErreurs="erreurs.xsl";
// başlıklar XML
echo "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
echo "<?xml-stylesheet type=\"text/xsl\" href=\"$xslErreurs\" ?>\n";
// hatalar
echo "<erreurs>\n";
for ($i=0;$i<count($requête[erreurs]);$i++){
// $i hatası
echo "<erreur>".$requête[erreurs][$i]."</erreur>";
}//for
echo "</erreurs>\n";
?>
Bu kod, $requête sözlüğünden, aşağıdakine benzer bir XML kodu oluşturulmasına olanak tanır:
<?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 stil sayfası, bu XML kodunu HTML koduna dönüştürecektir.
İlk örneği inceleyelim:

SGBD MySQL çalıştırılmıyor. Bunun üzerine şu yanıt alınır:

Tarayıcı tarafından alınan kaynak koduna bakıldığında şunu görürüz:
<?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>
Şimdi, SGBD ve MySQL komutlarını çalıştırıp birbirini izleyen çeşitli simülasyonlar yapıyoruz. Aşağıdaki yanıtı alıyoruz:

Tarayıcı tarafından alınan kod şöyledir:
<?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>
Yeni uygulamamızın, öncekine göre bakımı daha kolay olduğu dikkat çekmektedir. İşin bir kısmı XSL stil sayfalarına aktarılmıştır. Bu yeni görev dağılımının avantajı, yanıtların XML formatı bir kez belirlendikten sonra, stil sayfalarının geliştirilmesinin uygulama geliştirmesinden bağımsız hale gelmesidir.
5.3. XML formatındaki bir belgenin PHP formatına dönüştürülmesi
Vergi uygulamamızın bir sonraki sürümü, önceki xmlsimulations uygulamasının programlanmış bir istemcisi olacaktır. Dolayısıyla istemcimiz, XML kodunu alacak ve ilgilendiği bilgileri çıkarmak için bu kodu analiz etmek zorunda kalacaktır. Burada sürümlerimize bir ara verip, bir XML belgesini PHP formatında nasıl analiz edebileceğimizi öğreneceğiz. Bunu aşağıdaki örnek üzerinden yapacağız:
dos>e:\php43\php.exe xmlParser.php
syntaxe : xmlParser.php fichierXML
xmlParser.php uygulaması tek bir parametre kabul eder: analiz edilecek XML belgesinin URI (Uniform Resource Identifier) değeri. Örneğimizde, bu URI, xmlParser.php uygulamasının dizinine yerleştirilmiş bir XML dosyasının adı olacaktır. İki çalışma örneğini ele alalım. İlk örnekte, analiz edilen XML dosyası, aşağıdaki erreurs.xml dosyasıdır:
<?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>
Analiz şu sonuçları vermektedir:
dos>e:\php43\php.exe xmlParser.php erreurs.xml
ERREURS
ERREUR
[erreur 1]
/ERREUR
ERREUR
[erreur 2]
/ERREUR
/ERREURS
xmlParser.php uygulamasının ne işe yaradığını henüz açıklamamıştık, ancak burada, uygulamanın analiz edilen XML belgesinin yapısını görüntülediği görülüyor. İkinci örnekte, aşağıdaki XML ve simulations.xml dosyaları analiz ediliyor:
<?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>
Analiz şu sonuçları vermektedir:
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 uygulaması, web sunucusunun gönderebileceği hataları veya simülasyonları tespit edebildiği için, vergi uygulamamızda ihtiyacımız olan her şeyi içeriyor. Koduna bir göz atalım:
<?php
// $0 sözdizimi fichierXML
// dosyanın yapısını ve içeriğini gösterir fichierXML
// çağrı doğrulaması
if(count($argv)!=2){
// hata mesajı
fwrite(STDERR,"syntaxe : $argv[0] fichierXML\n");
// son
exit(1);
}//if
// başlatma işlemleri
$file=$argv[1]; // xml dosyası
$depth=0; // girinti seviyesi=ağaç yapısındaki derinlik
// program
// bir XML metin analiz nesnesi oluşturulur
$xml_parser=xml_parser_create();
// etiketin başında ve sonunda hangi işlevlerin yürütüleceği belirtilir
xml_set_element_handler($xml_parser,"startElement","endElement");
// verilerle karşılaşıldığında hangi işlevin çalıştırılacağı belirtilir
xml_set_character_data_handler($xml_parser,"afficheData");
// XML dosyasını okuma modunda açma
if (! ($fp=@fopen($file,"r"))){
fwrite(STDERR,"impossible d'ouvrir le fichier xml $file\n");
exit(2);
}//if
// XML dosyasının 4096 baytlık bloklar halinde işlenmesi
while($data=fread($fp,4096)){
// okunan verilerin analizi
if (! xml_parse($xml_parser,$data,feof($fp))){
// bir hata oluştu
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));
// son
exit(3);
}//if
}//while
// dosya taranmıştır
// xml ayrıştırıcısı tarafından kullanılan kaynaklar serbest bırakılıyor
xml_parser_free($xml_parser);
// son
exit(0);
// -----------------------------------------------------------
// başlangıç etiketiyle karşılaşıldığında çağrılan işlev
function startElement($parser,$name,$attributs){
global $depth;
// bir dizi boşluk (girinti)
for($i=0;$i<$depth;$i++){
print " ";
}//for
// öznitelikler
$précisions="";
while(list($attrib,$valeur)=each($attributs)){
$précisions.="($attrib,$valeur) ";
}
// etiket adı ve varsa öznitelikler görüntülenir
if($précisions)
print "$name,$précisions\n";
else print "$name\n";
// bir üst düzey daha
$depth++;
}//startElement
// -----------------------------------------------------------
// kapanış etiketiyle karşılaşıldığında çağrılan işlev
function endElement($parser,$name){
// etiket sonu
// girinti seviyesi
global $depth;
$depth--;
// bir dizi boşluk (girinti)
for($i=0;$i<$depth;$i++){
echo " ";
}//for
// etiket adı
echo "/$name\n";
}//endElement
// -----------------------------------------------------------
// veri görüntüleme işlevi
function afficheData($parser,$data){
// girinti seviyesi
global $depth;
// veriler görüntülenir
$data=trim($data);
if($data!=""){
// bir dizi boşluk (girinti)
for($i=0;$i<$depth;$i++){
echo " ";
}//for
echo "[$data]\n";
}//if
}//afficheData
?>
XML ile ilgili kodu inceleyelim. Bir XML belgesini analiz etmek için, uygulamamızın genellikle "parser" olarak adlandırılan bir XML kod analizcisine ihtiyacı vardır.
Ayrıştırıcı, XML belgesini ayrıştırırken şu tür olaylar üretir: belgenin başlangıcına ulaşıldı, bir etiket başlangıcı, bir etiket özniteliği, bir etiket içeriği, bir etiket sonu, belgenin sonu, ... Bu olayları, kendisine belirtilmesi gereken yöntemlere iletir:
<?php
...
// etiketin başında ve sonunda hangi işlevlerin yürütüleceği belirtilir
xml_set_element_handler($xml_parser,"startElement","endElement");
// verilerle karşılaşıldığında hangi işlevin çalıştırılacağı belirtilir
xml_set_character_data_handler($xml_parser,"afficheData");
ayrıştırıcı tarafından gönderilen olay işlemci | işleme yöntemi |
startElement($parser, $name, $attributs) işlevi $parser: belgenin ayrıştırıcısı $name: ayrıştırılan öğenin adı. Karşılaşılan öğe <simulations> ise, name="simulations" olur. $attributs: etiketin özniteliklerinin listesi, (ATTRIBUT,değer) biçimindedir; burada ATTRIBUT, etiketin büyük harflerle yazılmış adıdır. | |
function afficheData($parser,$data) $parser: belgenin ayrıştırıcısı $data: etiket verileri | |
function endElement($parser, $name) parametreler, startElement yöntemine aittir. |
startElement işlevi, $attributs parametresi sayesinde öğenin özniteliklerini almayı sağlar. Bu parametre, etiket özniteliklerinin bir sözlüğüdür. Dolayısıyla, aşağıdaki etiket varsa:
$attributs sözlüğü şu şekilde olacaktır: array(marie=>evet,çocuklar=>2,maaş=>200000,vergi=>22504)
Ayrıştırıcı ve yukarıdaki yöntemler tanımlandıktan sonra, bir belgenin ayrıştırılması xml_parse işleviyle gerçekleştirilir:
<?php
...
// XML dosyasını 4096 baytlık bloklar halinde işleme
while($data=fread($fp,4096)){
// Okunan verilerin analizi
if (! xml_parse($xml_parser,$data,feof($fp))){
...
}//if
}//while
function xml_parse($parser, $doc, $fin) $parser ayrıştırıcısı, $doc belgesini ayrıştırır. $doc, daha büyük bir belgenin bir parçası olabilir. $fin parametresi, bunun son parça olup olmadığını (true) veya olmadığını (false) belirtir. $doc belgesinin analiz edilmesi sırasında, xml_set_element_handler tarafından tanımlanan işlevler her etiket başlangıcında ve sonunda çağrılır. xml_set_character_data_handler ile tanımlanan işlev ise bir etiket içeriği her alındığında çağrılır. |
XML belgesinin analizi sırasında, özellikle XML belgesinin "hatalı biçimlendirilmiş" olması durumunda (örneğin, kapanış etiketlerinin eksik olması gibi) hatalar meydana gelebilir. Bu durumda, xml_parse işlevi "yanlış" olarak değerlendirilen bir değer döndürür:
<?php
...
if (! xml_parse($xml_parser,$data,feof($fp))){
// bir hata oluştu
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));
// son
exit(3);
}//if
function xml_get_error_code($parser) meydana gelen son hatanın kodunu döndürür -- function xml_error_string($code) parametre olarak verilen koda ait hata mesajını döndürür -- function xml_get_current_line($parser) parser tarafından analiz edilmekte olan XML belgesinin satır numarasını döndürür |
Belge analiz edildikten sonra, ayrıştırıcıya ayrılan kaynaklar serbest bırakılır:
<?php
...
// XML ayrıştırıcısı tarafından kullanılan kaynaklar serbest bırakılıyor
xml_parser_free($xml_parser);
function xml_free($parser) |
Bu açıklama yapıldıktan sonra, önceki program ve yürütme örnekleri kendiliğinden anlaşılır hale gelir.
5.4. Vergi uygulaması: sürüm 6
Artık, XML formatında veri üreten vergi hizmetimiz için programlanmış bir istemci yazmak üzere gerekli tüm unsurlara sahibiz. İstemciyi oluşturmak için uygulamamızın 4. sürümünü kullanacağız ve sunucu tarafında ise 5. sürümü koruyacağız. Bu istemci-sunucu uygulamasında:
- vergi hesaplama simülasyonu hizmeti, xmlsimulations.php uygulaması tarafından gerçekleştirilir. Dolayısıyla, sunucunun yanıtı, sürüm 5'te gördüğümüz gibi XML biçimindedir.
- İstemci artık bir tarayıcı değil, bağımsız bir PHP istemcisidir. Konsol arayüzü, sürüm 4'tekiyle aynıdır.
Okuyucunun, sürüm 4 için programlanmış istemci olan cltImpots.php uygulamasının kodunu tekrar gözden geçirmesi önerilir. Bu istemci, sunucudan $document adlı bir belge alırdı. Bu belge o zamanlar HTML biçimindeydi. Artık bir XML belgesidir. HTML ve $document belgeleri aşağıdaki işlev tarafından analiz ediliyordu:
<?php
...
function getInfos($document){
// $document : belge HTML
// hatalar listesi aranır
// ya da simülasyon tablosu aranır
// sonucun hazırlanması
$impots[erreurs]=array();
$impots[simulations]=array();
........
return $impots;
}//getInfos
İşlev, HTML $document belgesini aldı, analiz etti ve iki özniteliğe sahip bir $impots sözlüğü döndürdü:
- hatalar: bir hata tablosu
- simülasyonlar: bir simülasyon tablosu; her simülasyonun kendisi dört öğeden oluşan bir tablodur (eş, çocuklar, maaş, vergi).
cltImpots.php uygulaması artık cltXmlSimulations.php olarak adlandırılıyor. Artık bunun bir XML belgesi olduğu gerçeğini hesaba katmak için, yalnızca sunucudan alınan belgeyi işleyen kısım değiştirilmelidir. Böylece getInfos işlevi şu şekilde olur:
<?php
...
// --------------------------------------------------------------
function getInfos($document){
// $document : XML belgesi
// ya hata listesi aranıyor
// ya da simülasyon tablosu aranıyor
global $impots,$balises;
// sonucun hazırlanması
$impots[erreurs]=array();
$impots[simulations]=array();
// işlenmekte olan etiketler
$balises=array();
// bir xml metin analizi nesnesi oluşturuluyor
$xml_parser=xml_parser_create();
// etiketin başında ve sonunda hangi işlevlerin çalıştırılacağı belirtilir
xml_set_element_handler($xml_parser,"startElement","endElement");
// verilerle karşılaşıldığında hangi işlevin çalıştırılacağı belirtiliyor
xml_set_character_data_handler($xml_parser,"getData");
// $document'in analizi yapılır
xml_parse($xml_parser,$document);
// XML analizörü tarafından işgal edilen kaynaklar serbest bırakılır
xml_parser_free($xml_parser);
// son
return $impots;
}//getInfos
// -----------------------------------------------------------
// başlangıç etiketiyle karşılaşıldığında çağrılan işlev
function startElement($parser,$name,$attributs){
global $impots,$balise,$balises,$contenu;
// etiketin adı ve içeriği kaydedilir
$balise=strtolower($name);
$contenu="";
// etiket yığınına eklenir
array_push($balises,$balise);
// Bu bir simülasyon mu?
if($balise=="simulation"){
// simülasyonun özellikleri kaydedilir
$impots[simulations][]=array($attributs[MARIE],$attributs[ENFANTS],$attributs[SALAIRE],$attributs[IMPOT]);
}//if
}//startElement
// -----------------------------------------------------------
// son etiketiyle karşılaşıldığında çağrılan işlev
function endElement($parser,$name){
// geçerli etiketi alınır
global $impots,$balises,$contenu;
$balise=array_pop($balises);
// bu bir hata etiketi mi?
if($balise=="erreur"){
// bir hata daha
$impots[erreurs][]=trim($contenu);
}//if
}//endElement
// -----------------------------------------------------------
// bir etiket içeriğini işleme işlevi
function getData($parser,$data){
// genel veriler
global $balise,$contenu;
// bu bir hata etiketi mi?
if($balise=="erreur"){
// geçerli etiketin içeriğine eklenir
$contenu.=$data;
}//if
}//getData
Açıklamalar:
- getInfos($document) işlevi, önce bir ayrıştırıcı oluşturur, ardından onu yapılandırır ve son olarak $document belgesinin ayrıştırılmasını başlatır:
<?php
...
// bir XML metin analiz nesnesi oluşturulur
$xml_parser=xml_parser_create();
// etiketin başında ve sonunda hangi işlevlerin yürütüleceği belirtilir
xml_set_element_handler($xml_parser,"startElement","endElement");
// verilerle karşılaşıldığında hangi işlevin çalıştırılacağı belirtilir
xml_set_character_data_handler($xml_parser,"getData");
// $document'in analizi yapılır
xml_parse($xml_parser,$document);
- Analiz tamamlandığında, ayrıştırıcıya ayrılan kaynaklar serbest bırakılır ve $impots sözlüğü iade edilir.
<?php
...
// XML analizörü tarafından işgal edilen kaynaklar serbest bırakılır
xml_parser_free($xml_parser);
// son
return $impots;
- startElement($parser,$name,$attributs) işlevi, her etiket başlangıcında çağrılır. Bu işlev
- $name etiketini, $balises etiket tablosunda kaydeder. Bu tablo bir yığın olarak yönetilir: etiket sonu sembolüyle karşılaşıldığında, $balises'te yığına en son eklenen etiket yığından çıkarılır. Geçerli etiket ayrıca $balise'e kaydedilir. $attributs sözlüğünde, karşılaşılan etiketin öznitelikleri bulunur; bu öznitelikler büyük harflerle yazılır.
- simülasyon etiketi ise, öznitelikleri genel sözlük $impots[simulations]'e kaydeder.
- Bir etiketin içeriği $data olarak alındığında, getData($parser,$data) işlevi çağrılır. Burada bir önlem alınmıştır. Bazı API'lerde, özellikle Java gibi XML belge işleme ortamlarında, bu fonksiyonun tekrar tekrar çağrılabileceği belirtilmiştir, c.a.d. Bir etiketin içeriğinin tek seferde elde edilemeyebileceği belirtilmiştir. Burada, PHP belgelerinde bu kısıtlama belirtilmemiştir. Tedbir amacıyla, elde edilen $data değeri bir küresel değişkene $contenu eklenir. Ancak etiket sonu sembolüyle karşılaşıldığında, etiketin içeriğinin tamamının elde edildiği kabul edilir. Bu işlemden etkilenen tek etiket <erreur> etiketidir.
- endElement($parser,$name) işlevi, her etiket sonu geldiğinde çağrılır. Bu işlev burada, etiket yığınından son etiketi kaldırarak geçerli etiketin adını değiştirmek ve <erreur> etiketinin içeriğini, $impots[erreurs] dizisine eklemek için kullanılır.
İşte birkaç çalışma örneği; ilk olarak, başlatılmamış bir SGBD ile:
dos>e:\php43\php.exe cltXmlSimulations.php http://localhost/poly/impots/8/xmlsimulations.php evet 2 200000
Jeton de session=[e8c29ea12f79e4771960068d161229fd]
Les erreurs suivantes se sont produites :
Impossible d'ouvrir la base DSN [mysql-dbimpots] (S1000)
Ardından çalıştırılan SGBD ile:
dos>e:\php43\php.exe cltXmlSimulations.php http://localhost/poly/impots/8/xmlsimulations.php evet 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 evet 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 hayır 2 200000 69a54d79db10b70ed0a2d55d5026ac8b
Jeton de session=[69a54d79db10b70ed0a2d55d5026ac8b]
Simulations :
[oui,3,200000,16400]
[oui,2,200000,22504]
[non,2,200000,33388]
5.5. Sonuç
XML yanıtı sayesinde, vergi uygulaması hem geliştiricisi hem de istemci uygulamalarının geliştiricileri için daha kolay yönetilebilir hale geldi.
- Artık sunucu uygulamasının tasarımı iki tür kişiye emanet edilebilir: servlet geliştiricisi ve tarayıcılarda yanıtın görünümünü yönetecek grafik tasarımcı. Grafik tasarımcının, buna eşlik edecek stil sayfalarını oluşturmak için sunucunun yanıt yapısını bilmesi yeterlidir. Bu stil sayfalarının, PHP uygulamasından ayrı ve bağımsız XSL dosyaları halinde tutulduğunu hatırlatalım. Dolayısıyla grafik tasarımcı, geliştiriciden bağımsız olarak çalışabilir.
- Müşteri uygulamalarının tasarımcıları da sadece sunucunun XML yanıtının yapısını bilmek zorundadır. Grafik tasarımcının stil sayfalarında yapabileceği değişiklikler, her zaman aynı kalan bu XML yanıtı üzerinde hiçbir etki yaratmaz. Bu çok büyük bir avantajdır.
- Geliştirici, her şeyi bozmadan PHP uygulamasını nasıl geliştirebilir? Öncelikle, XML yanıtı değişmediği sürece, uygulamasını istediği gibi düzenleyebilir. Ayrıca, müşterilerinin beklediği <hata> ve <simülasyon> öğelerini koruduğu sürece XML yanıtını da geliştirebilir. Böylece bu yanıta yeni etiketler ekleyebilir. Grafik tasarımcı bunları stil sayfalarında dikkate alacak ve tarayıcılar yanıtın yeni sürümlerini alabilecektir. Programlanmış istemciler ise eski modelle çalışmaya devam edecek; yeni etiketler basitçe göz ardı edilecektir. Bunun mümkün olabilmesi için, sunucunun yanıtının XML analizinde aranan etiketlerin doğru bir şekilde tanımlanmış olması gerekir. Vergi uygulamasındaki XML istemcimizde de bu şekilde yapılmıştır; burada prosedürlerde, özellikle <erreur> ve <simulation> etiketlerinin işleneceği belirtilmiştir. Dolayısıyla diğer etiketler göz ardı edilir.