5. XML und PHP
In diesem Kapitel geben wir eine Einführung in die Verwendung von XML-Dokumenten (eXtensible Markup Language) mit PHP. Wir tun dies im Zusammenhang mit der im vorigen Kapitel besprochenen Steueranwendung.
5.1. XML-Dateien und XSL-Stylesheets
Betrachten Sie die folgende XML-Datei, die die Ergebnisse von Simulationen darstellen könnte:
<?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>
Bei der Anzeige mit IE 6 ergibt sich folgendes Ergebnis:

IE6 erkennt, dass es sich um eine XML-Datei handelt (dank der Dateiendung .xml), und formatiert sie auf seine eigene Weise. Mit Netscape erhält man eine leere Seite. Wenn man sich jedoch den Quellcode ansieht (Ansicht/Quelltext), sieht man die ursprüngliche XML-Datei:

Warum zeigt Netscape nichts an? Weil es ein Stylesheet benötigt, das ihm mitteilt, wie die XML-Datei in eine HTML-Datei umgewandelt werden soll, die es dann anzeigen kann. Es stellt sich heraus, dass IE 6 über ein Standard-Stylesheet verfügt, wenn die XML-Datei kein eigenes bereitstellt, was hier der Fall war.
Es gibt eine Sprache namens XSL (eXtensible StyleSheet Language), mit der man die Umwandlungen beschreiben kann, die erforderlich sind, um eine XML-Datei in eine beliebige Textdatei zu konvertieren. XSL unterstützt zahlreiche Anweisungen und ähnelt stark Programmiersprachen. Wir werden hier nicht ins Detail gehen, da dies Dutzende von Seiten in Anspruch nehmen würde. Wir werden lediglich zwei Beispiele für XSL-Stylesheets beschreiben. Das erste ist dasjenige, das die XML-Datei simulations.xml in HTML-Code umwandelt. Wir ändern letztere so, dass sie das Stylesheet angibt, das Browser verwenden können, um sie in ein HTML-Dokument umzuwandeln, das sie anzeigen können:
<?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>
Der XML-Befehl
bezeichnet die Datei simulations.xsl als XML-Stylesheet vom Typ text/xsl, d. h. als Textdatei, die XSL-Code enthält. Dieses Stylesheet wird von Browsern verwendet, um den XML-Text in ein HTML-Dokument umzuwandeln. Hier ist das Ergebnis, das mit Netscape 7 beim Laden der XML-Datei simulations.xml erzielt wurde:

Wenn wir den Quellcode des Dokuments anzeigen (Ansicht/Quelltext), sehen wir das ursprüngliche XML-Dokument und nicht das angezeigte HTML-Dokument:

Netscape verwendete das Stylesheet „simulations.xsl“, um das obige XML-Dokument in ein anzeigbares HTML-Dokument umzuwandeln. Nun ist es an der Zeit, uns den Inhalt dieses Stylesheets anzusehen:
<?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>
- Ein XSL-Stylesheet ist eine XML-Datei und folgt daher den XML-Regeln. Unter anderem muss es „wohlgeformt“ sein, was bedeutet, dass jeder öffnende Tag geschlossen werden muss.
- Die Datei beginnt mit zwei XML-Direktiven, die in jedem XSL-Stylesheet unter Windows enthalten sein können:
<?xml version="1.0" encoding="windows-1252"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Das Attribut „encoding=“windows-1252““ ermöglicht die Verwendung von Zeichen mit Akzenten im Stylesheet.
- Das Tag <xsl:output method="html" indent="yes"/> teilt dem XSL-Interpreter mit, dass wir „eingerückten“ HTML-Code erzeugen möchten.
- Das Tag <xsl:template match="element"> dient dazu, das Element im XML-Dokument zu definieren, auf das die Anweisungen zwischen <xsl:template ...> und </xsl:template> angewendet werden sollen.
Im obigen Beispiel bezieht sich das Element „/“ auf die Wurzel des Dokuments. Das bedeutet, dass die zwischen den beiden Tags befindlichen XSL-Befehle ausgeführt werden, sobald der Anfang des XML-Dokuments erreicht ist.
- Alles, was kein XSL-Tag ist, wird unverändert in den Ausgabestrom übernommen. Die XSL-Tags selbst werden ausgeführt. Einige von ihnen erzeugen ein Ergebnis im Ausgabestrom. Betrachten wir das folgende Beispiel:
<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>
Beachten Sie, dass das zu analysierende XML-Dokument wie folgt lautet:
<?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>
Ab dem Anfang des geparsten XML-Dokuments (match="/") gibt der XSL-Prozessor den Text
<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>
Beachten Sie, dass im Originaltext <hr/> stand und nicht <hr>. Im Originaltext konnten wir <hr> nicht schreiben, da es zwar ein gültiger HTML-Tag, aber ein ungültiger XML-Tag ist. Wir haben es hier jedoch mit XML-Text zu tun, der „wohlgeformt“ sein muss, was bedeutet, dass jeder Tag geschlossen werden muss. Wir schreiben daher <hr/>, und da wir <xsl:output text="html ...> geschrieben haben, wandelt der Interpreter den Text <hr/> in <hr> um. Auf diesen Text folgt der durch den XSL-Befehl erzeugte Text:
Wir werden später sehen, um welchen Text es sich dabei handelt. Schließlich fügt der Interpreter den Text hinzu:
Der Befehl <xsl:apply-templates select="/simulations/simulation"/> weist den Interpreter an, die „Vorlage“ auf das Element /simulations/simulation anzuwenden. Er wird jedes Mal ausgeführt, wenn der XSL-Interpreter innerhalb eines <simulations>..</simulations>-Tags im geparsten XML-Text auf ein <simulation>..</simulations>- oder <simulation/>-Tag stößt. Wenn der Interpreter auf ein solches Tag stößt, führt er die Anweisungen der folgenden Vorlage aus:
<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>
Betrachten Sie die folgenden XML-Zeilen:
Die Zeile <simulation ..> entspricht der Vorlage für die XSL-Anweisung <xsl:apply-templates select="/simulations/simulation">. Der XSL-Interpreter wird daher versuchen, die Anweisungen, die dieser Vorlage entsprechen, darauf anzuwenden. Er findet die Vorlage <xsl:template match="simulation"> und führt sie aus. Zur Erinnerung: Alles, was kein XSL-Befehl ist, wird vom XSL-Interpreter unverändert weitergeleitet, während XSL-Befehle durch das Ergebnis ihrer Ausführung ersetzt werden. Die XSL-Anweisung <xsl:value-of select="@champ"/> wird somit durch den Wert des Attributs „champ“ des geparsten Knotens (hier ein <simulation>-Knoten) ersetzt. Das Parsen der vorherigen XML-Zeile erzeugt die folgende Ausgabe:
XSL | Ausgabe |
<tr><td> | <tr><td> |
<xsl:value-of select="@marie"/> | ja |
</td><td> | </td><td> |
<xsl:value-of select="@children"/> | 2 |
</td><td> | </td><td> |
<xsl:value-of select="@salary"/> | 200000 |
</td><td> | </td><td> |
<xsl:value-of select="@tax"/> | 22504 |
</td></tr> | </td></tr> |
Insgesamt lautet die XML-Zeile
wird in die folgende HTML-Zeile umgewandelt:
All diese Erklärungen sind zwar etwas rudimentär, aber dem Leser sollte nun klar sein, dass der folgende XML-Text:
<?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>
begleitet von dem folgenden XSL-Stylesheet 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>
erzeugt den folgenden HTML-Text:
<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>
Die folgende XML-Datei 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>
Bei der Anzeige in einem modernen Browser (hier Netscape 7) wird dies wie folgt dargestellt:

5.2. Steueranwendung: Version 5
5.2.1. Die XML-Dateien und XSL-Stylesheets der Steueranwendung
Kehren wir zu unserem Ausgangspunkt zurück, nämlich der Steuer-Webanwendung, und erinnern wir uns daran, dass wir sie so ändern wollen, dass die an die Clients gesendete Antwort im XML-Format statt im HTML-Format vorliegt. Dieser HTML-Antwort wird ein XSL-Stylesheet beigefügt, damit Browser sie anzeigen können. Im vorigen Absatz haben wir Folgendes vorgestellt:
- die Datei simulations.xml, die einen Prototyp einer XML-Antwort mit Simulationen zur Steuerberechnung enthält
- die Datei simulations.xsl, die das dieser XML-Antwort beigefügte XSL-Stylesheet sein wird
Wir müssen auch den Fall einer Antwort berücksichtigen, die Fehler enthält. Der Prototyp für die XML-Antwort in diesem Fall ist die folgende Datei errors.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>
Das Stylesheet „errors.xsl“, das zur Anzeige dieses XML-Dokuments in einem Browser verwendet wird, sieht wie folgt aus:
<?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>
Dieses Stylesheet führt einen bisher noch nicht behandelten XSL-Befehl ein: <xsl:value-of select="."/>. Dieser Befehl gibt den Wert des analysierten Knotens aus, in diesem Fall eines <error>text</error>-Knotens. Der Wert dieses Knotens ist der Text zwischen dem öffnenden und dem schließenden Tag, in diesem Fall „text“.
Der Code in errors.xml wird durch das Stylesheet errors.xsl in das folgende HTML-Dokument umgewandelt:
<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>
Die Datei „errors.xml“ wird zusammen mit ihrem Stylesheet von einem Browser wie folgt angezeigt:

5.2.2. Die Anwendung „xmlsimulations“
Wir erstellen eine Datei „xmlsimulations.html“ und legen sie im Anwendungsverzeichnis „imports“ ab. Die angezeigte Seite sieht wie folgt aus:

Dieses HTML-Dokument ist ein statisches Dokument. Sein Code lautet wie folgt:
<html>
<head>
<title>impots</title>
<script language="JavaScript" type="text/javascript">
function effacer(){
// raz du formulaire
with(document.frmImpots){
optMarie[0].checked=false;
optMarie[1].checked=true;
txtEnfants.value="";
txtSalaire.value="";
txtImpots.value="";
}//with
}//effacer
function calculer(){
// vérification des paramètres avant de les envoyer au serveur
with(document.frmImpots){
//nbre d'enfants
champs=/^\s*(\d+)\s*$/.exec(txtEnfants.value);
if(champs==null){
// le modéle n'est pas vérifié
alert("Le nombre d'enfants n'a pas été donné ou est incorrect");
nbEnfants.focus();
return;
}//if
//salaire
champs=/^\s*(\d+)\s*$/.exec(txtSalaire.value);
if(champs==null){
// le modéle n'est pas vérifié
alert("Le salaire n'a pas été donné ou est incorrect");
salaire.focus();
return;
}//if
// c'est bon - on envoie
submit();
}//with
}//calculer
</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>
Beachten Sie, dass die Formulardaten an die URL /poly/impots/7/xmlsimulations.php gesendet werden. Der Code für die Anwendung xmlsimulations.php ist dem der Anwendung impots.php sehr ähnlich. Dem Leser wird empfohlen, sich letztere anzusehen. Hier noch einmal der Eingabecode:
<?php
// processes the tax form
// libraries
include "ImpotsDSN.php";
// session start
session_start();
// application configuration
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);
// retrieve session parameters
$session=$_SESSION["session"];
// valid session?
if(! isset($session) || ! isset($session[objImpots]) || ! isset($session[simulations])){
// start a new session
$session=array(objImpots=>new ImpotsDSN($bdImpots),simulations=>array());
// mistakes?
if(count($session[objImpots]->erreurs)!=0){
$requête=array(erreurs=>$session[objImpots]->erreurs);
// error page display
include $erreursImpots;
// end
$session=array();
terminerSession($session);
}//if
}//if
// retrieve the parameters of the current exchange
$requête[marié]=$_POST["optMarie"];
$requête[enfants]=$_POST["txtEnfants"];
$requête[salaire]=$_POST["txtSalaire"];
// do we have all the parameters?
if(! isset($requête[marié]) || ! isset($requête[enfants]) || ! isset($requête[salaire])){
// empty form display
$requête=array(chkoui=>"",chknon=>"checked",enfants=>"",salaire=>"",impots=>"",
erreurs=>array(),simulations=>array());
include $formulaireImpots;
// end
terminerSession($session);
}//if
// checking parameters
$requête=vérifier($requête);
// mistakes?
if(count($requête[erreurs])!=0){
// form display
include "$formulaireImpots";
// end
terminerSession($session);
}//if
// calculating tax payable
$requête[impots]=$session[objImpots]->calculer(array(marié=>$requête[marié],
enfants=>$requête[enfants],salaire=>$requête[salaire]));
// one more simulation
$session[simulations][]=array($requête[marié],$requête[enfants],$requête[salaire],$requête[impots]);
$requête[simulations]=$session[simulations];
// form display
include "$formulaireImpots";
// end
terminerSession($session);
...
Die HTML-Seiten wurden mithilfe der „include“-Zeilen angezeigt. Hier möchten wir nun XML anstelle von HTML generieren. Erstellen Sie dazu einfach zwei neue Dateien, „impots_erreurs.php“ und „impots_simulations.php“, um XML anstelle von HTML zu generieren. Der Rest der Anwendung bleibt unverändert. Der Code sieht dann wie folgt aus:
<?php
// processes the tax form
// libraries
include "ImpotsDSN.php";
// session start
session_start();
// application configuration
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);
// retrieve session parameters
$session=$_SESSION["session"];
// valid session?
if(! isset($session) || ! isset($session[objImpots]) || ! isset($session[simulations])){
// start a new session
$session=array(objImpots=>new ImpotsDSN($bdImpots),simulations=>array());
// mistakes?
if(count($session[objImpots]->erreurs)!=0){
$requête=array(erreurs=>$session[objImpots]->erreurs);
// display error page in XML format
header("Content-type: text/xml");
include $erreursImpots;
// end
$session=array();
terminerSession($session);
}//if
}//if
// retrieve the parameters of the current exchange
$requête[marié]=$_POST["optMarie"];
$requête[enfants]=$_POST["txtEnfants"];
$requête[salaire]=$_POST["txtSalaire"];
// do we have all the parameters?
if(! isset($requête[marié]) || ! isset($requête[enfants]) || ! isset($requête[salaire])){
// empty form display
include $formulaireImpots;
// end
terminerSession($session);
}//if
// checking parameters
$requête=vérifier($requête);
// mistakes?
if(count($requête[erreurs])!=0){
// display errors in XML format
header("Content-type: text/xml");
include "$erreursImpots";
// end
terminerSession($session);
}//if
// calculation of tax payable
$requête[impots]=$session[objImpots]->calculer(array(marié=>$requête[marié],
enfants=>$requête[enfants],salaire=>$requête[salaire]));
// one more simulation
$session[simulations][]=array($requête[marié],$requête[enfants],$requête[salaire],$requête[impots]);
$requête[simulations]=$session[simulations];
// display simulations in XML format
header("Content-type: text/xml");
include "$simulationsImpots";
// end
terminerSession($session);
Wir haben zuvor die beiden Arten von XML-Antworten, die bereitgestellt werden sollen, sowie die dazugehörigen Stylesheets vorgestellt und untersucht. Der Code für die Anwendung impots_simulations.php lautet wie folgt:
<?php
// generates XML code for impots application simulations page
// some constants
$xslSimulations="simulations.xsl";
// headers XML
echo "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
echo "<?xml-stylesheet type=\"text/xsl\" href=\"$xslSimulations\" ?>\n";
// simulations
echo "<simulations>\n";
for ($i=0;$i<count($requête[simulations]);$i++){
// simulation $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";
?>
Dieser Code verwendet das $query-Dictionary, um XML-Code zu generieren, der in etwa wie folgt aussieht:
<?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>
Das Stylesheet „simulations.xsl“ wandelt diesen XML-Code in HTML-Code um.
Der Code für die Anwendung impots_erreurs.php lautet wie folgt:
<?php
// generates code XML for impots application error page
// some constants
$xslErreurs="erreurs.xsl";
// headers XML
echo "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
echo "<?xml-stylesheet type=\"text/xsl\" href=\"$xslErreurs\" ?>\n";
// errors
echo "<erreurs>\n";
for ($i=0;$i<count($requête[erreurs]);$i++){
// error $i
echo "<erreur>".$requête[erreurs][$i]."</erreur>";
}//for
echo "</erreurs>\n";
?>
Dieser Code verwendet das $request-Dictionary, um XML-Code zu generieren, der in etwa wie folgt aussieht:
<?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>
Das Stylesheet „errors.xsl“ wandelt diesen XML-Code in HTML-Code um.
Sehen wir uns ein erstes Beispiel an:

Das MySQL-DBMS läuft nicht. Wir erhalten dann die folgende Antwort:

Wenn wir uns den vom Browser empfangenen Quellcode ansehen, sehen wir Folgendes:
<?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>
Nun starten wir das MySQL-DBMS und führen eine Reihe von Simulationen durch. Wir erhalten folgende Antwort:

Der vom Browser empfangene Code lautet wie folgt:
<?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>
Beachten Sie, dass unsere neue Anwendung einfacher zu warten ist als die vorherige. Ein Teil der Arbeit wurde auf die XSL-Stylesheets verlagert. Der Vorteil dieser neuen Aufgabenteilung besteht darin, dass die Entwicklung der Stylesheets, sobald das XML-Format der Antworten festgelegt ist, unabhängig von der Entwicklung der Anwendung erfolgt.
5.3. Parsen eines XML-Dokuments in PHP
Die nächste Version unserer Steueranwendung wird ein Client-Programm für die vorherige Anwendung „xmlsimulations“ sein. Unser Client erhält daher XML-Code, den er parsen muss, um die benötigten Informationen zu extrahieren. Wir machen nun eine Pause von unseren verschiedenen Versionen und lernen, wie man ein XML-Dokument in PHP parst. Dazu verwenden wir das folgende Beispiel:
dos>e:\php43\php.exe xmlParser.php
syntaxe : xmlParser.php fichierXML
Die Anwendung xmlParser.php akzeptiert einen Parameter: die URI (Uniform Resource Identifier) des zu parsten XML-Dokuments. In unserem Beispiel ist diese URI einfach der Name einer XML-Datei, die sich im Verzeichnis der Anwendung xmlParser.php befindet. Betrachten wir zwei Ausführungsbeispiele. Im ersten Beispiel ist die zu parste XML-Datei die folgende Datei errors.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>
Die Analyse liefert folgende Ergebnisse:
dos>e:\php43\php.exe xmlParser.php erreurs.xml
ERREURS
ERREUR
[erreur 1]
/ERREUR
ERREUR
[erreur 2]
/ERREUR
/ERREURS
Wir haben noch nicht beschrieben, was die Anwendung xmlParser.php macht, aber hier sehen wir, dass sie die Struktur des geparsten XML-Dokuments anzeigt. Das zweite Beispiel parst die folgende XML-Datei, 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>
Die Analyse liefert folgende Ergebnisse:
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
Die Anwendung xmlParser.php enthält alles, was wir für unsere Steueranwendung benötigen, da sie sowohl die Fehler als auch die Simulationen abrufen konnte, die der Webserver möglicherweise sendet. Sehen wir uns den Code an:
<?php
// syntax $0 fichierXML
// displays the structure and contents of the fichierXML file
// call verification
if(count($argv)!=2){
// error msg
fwrite(STDERR,"syntaxe : $argv[0] fichierXML\n");
// end
exit(1);
}//if
// initializations
$file=$argv[1]; // the xml file
$depth=0; // indentation level=depth in tree structure
// the program
// create an xml text analysis object
$xml_parser=xml_parser_create();
// indicate which functions to execute at the beginning and end of the tag
xml_set_element_handler($xml_parser,"startElement","endElement");
// indicates which function to execute when data is encountered
xml_set_character_data_handler($xml_parser,"afficheData");
// open xml file in read mode
if (! ($fp=@fopen($file,"r"))){
fwrite(STDERR,"impossible d'ouvrir le fichier xml $file\n");
exit(2);
}//if
// xml file processing in blocks of 4096 bytes
while($data=fread($fp,4096)){
// analysis of read data
if (! xml_parse($xml_parser,$data,feof($fp))){
// an error has occurred
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));
// end
exit(3);
}//if
}//while
// the file has been browsed
// free up resources occupied by the xml parser
xml_parser_free($xml_parser);
// end
exit(0);
// -----------------------------------------------------------
// function called when a start tag is encountered
function startElement($parser,$name,$attributs){
global $depth;
// a sequence of spaces (indentation)
for($i=0;$i<$depth;$i++){
print " ";
}//for
// attributes
$précisions="";
while(list($attrib,$valeur)=each($attributs)){
$précisions.="($attrib,$valeur) ";
}
// displays the tag name and any attributes
if($précisions)
print "$name,$précisions\n";
else print "$name\n";
// an extra level of tree structure
$depth++;
}//startElement
// -----------------------------------------------------------
// the function called when an end tag is encountered
function endElement($parser,$name){
// end of tag
// indentation level
global $depth;
$depth--;
// a sequence of spaces (indentation)
for($i=0;$i<$depth;$i++){
echo " ";
}//for
// tag name
echo "/$name\n";
}//endElement
// -----------------------------------------------------------
// data display function
function afficheData($parser,$data){
// indentation level
global $depth;
// data are displayed
$data=trim($data);
if($data!=""){
// a sequence of spaces (indentation)
for($i=0;$i<$depth;$i++){
echo " ";
}//for
echo "[$data]\n";
}//if
}//afficheData
?>
Sehen wir uns den Code im Zusammenhang mit XML an. Um ein XML-Dokument zu parsen, benötigt unsere Anwendung einen XML-Parser.
Wenn der Parser das XML-Dokument analysiert, löst er Ereignisse aus wie: Ich bin auf den Anfang des Dokuments gestoßen, auf den Anfang eines Tags, auf ein Tag-Attribut, auf den Inhalt eines Tags, auf das Ende eines Tags, auf das Ende des Dokuments, ... Er leitet diese Ereignisse an Methoden weiter, die wir festlegen müssen:
<?php
...
// indicate which functions to execute at the beginning and end of the tag
xml_set_element_handler($xml_parser,"startElement","endElement");
// indicates which function to execute when data is encountered
xml_set_character_data_handler($xml_parser,"afficheData");
Ereignis, das vom Parser | Verarbeitungsmethode |
Funktion startElement($parser, $name, $attributes) $parser: der Dokument-Parser $name: Name des geparsten Elements. Wenn das gefundene Element <simulations> ist, lautet der Name „simulations“. $attributes: Liste der Attribute des Tags in der Form (ATTRIBUTE,Wert), wobei ATTRIBUTE der Name des Tags in Großbuchstaben ist. | |
Funktion displayData($parser, $data) $parser: der Dokument-Parser $data: die Daten des Tags | |
Funktion endElement($parser, $name) Die Parameter sind dieselben wie bei der Methode startElement. |
Die Funktion startElement ruft die Attribute des Elements über den Parameter $attributes ab. Dieser Parameter ist ein Wörterbuch der Attribute des Tags. Wenn wir also das folgende Tag haben:
dann sieht das $attributes-Wörterbuch wie folgt aus: array(marie=>yes,children=>2,salary=>200000,tax=>22504)
Sobald der Parser und die vorangehenden Methoden definiert sind, wird ein Dokument mit der Funktion xml_parse geparst:
<?php
...
// xml file processing in blocks of 4096 bytes
while($data=fread($fp,4096)){
// analysis of read data
if (! xml_parse($xml_parser,$data,feof($fp))){
...
}//if
}//while
Funktion xml_parse($parser, $doc, $end) Der Parser $parser analysiert das Dokument $doc. $doc kann ein Fragment eines größeren Dokuments sein. Der Parameter $end gibt an, ob dies der letzte Teil ist (true) oder nicht (false). Beim Parsen des Dokuments $doc werden die durch xml_set_element_handler definierten Funktionen am Anfang und am Ende jedes Tags aufgerufen. Die durch xml_set_character_data_handler definierte Funktion wird jedes Mal aufgerufen, wenn der Inhalt eines Tags abgerufen wird. |
Während des Parsens des XML-Dokuments können Fehler auftreten, insbesondere wenn das XML-Dokument „fehlformatiert“ ist, beispielsweise wenn schließende Tags fehlen. In diesem Fall gibt die Funktion xml_parse den Wert „false“ zurück:
<?php
...
if (! xml_parse($xml_parser,$data,feof($fp))){
// an error has occurred
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));
// end
exit(3);
}//if
function xml_get_error_code($parser) gibt den Code des zuletzt aufgetretenen Fehlers zurück -- Funktion xml_error_string($code) gibt die Fehlermeldung zurück, die dem als Parameter übergebenen Code zugeordnet ist -- Funktion xml_get_current_line($parser) gibt die Zeilennummer des XML-Dokuments zurück, das gerade geparst wird |
Sobald das Dokument geparst wurde, werden die dem Parser zugewiesenen Ressourcen freigegeben:
Funktion xml_free($parser) |
Mit dieser Erläuterung sind das vorangegangene Programm und seine Ausführungsbeispiele selbsterklärend.
5.4. Steueranwendung: Version 6
Wir verfügen nun über alle Elemente, um ein Client-Programm für unseren Steuerdienst zu schreiben, der XML liefert. Wir werden Version 4 unserer Anwendung für den Client verwenden und Version 5 für den Server beibehalten. In dieser Client-Server-Anwendung:
- wird der Dienst zur Simulation der Steuerberechnung von der Anwendung xmlsimulations.php übernommen. Die Antwort des Servers liegt daher im XML-Format vor, wie wir es in Version 5 gesehen haben.
- Der Client ist kein Browser mehr, sondern ein eigenständiger PHP-Client. Seine Konsolenoberfläche entspricht der von Version 4.
Der Leser ist eingeladen, sich den Code der Anwendung cltImpots.php anzusehen, die das Client-Programm für Version 4 war. Sie empfing ein $document vom Server. Dies war damals ein HTML-Dokument. Jetzt ist es ein XML-Dokument. Das HTML-Dokument $document wurde von der folgenden Funktion geparst:
<?php
...
function getInfos($document){
// $document : document HTML
// we look for either the list of errors
// or the simulation table
// preparing the result
$impots[erreurs]=array();
$impots[simulations]=array();
........
return $impots;
}//getInfos
Die Funktion hat das HTML-Dokument $document empfangen, es geparst und ein Dictionary $taxes mit zwei Attributen zurückgegeben:
- errors: ein Array mit Fehlern
- simulations: ein Array von Simulationen, wobei jede Simulation selbst ein Array mit vier Elementen ist (Ehepartner, Kinder, Gehalt, Steuer).
Die Anwendung cltImpots.php wird nun in cltXmlSimulations.php umbenannt. Es muss lediglich der Teil geändert werden, der das vom Server empfangene Dokument verarbeitet, um der Tatsache Rechnung zu tragen, dass es sich nun um ein XML-Dokument handelt. Die Funktion getInfos sieht dann wie folgt aus:
<?php
...
// --------------------------------------------------------------
function getInfos($document){
// $document : document XML
// we look for either the list of errors
// or the simulation table
global $impots,$balises;
// preparing the result
$impots[erreurs]=array();
$impots[simulations]=array();
// beacons in progress
$balises=array();
// create an xml text analysis object
$xml_parser=xml_parser_create();
// indicate which functions to execute at the beginning and end of the tag
xml_set_element_handler($xml_parser,"startElement","endElement");
// indicates which function to execute when data is encountered
xml_set_character_data_handler($xml_parser,"getData");
// we analyze $document
xml_parse($xml_parser,$document);
// free up resources occupied by the xml parser
xml_parser_free($xml_parser);
// end
return $impots;
}//getInfos
// -----------------------------------------------------------
// function called when a start tag is encountered
function startElement($parser,$name,$attributs){
global $impots,$balise,$balises,$contenu;
// note the tag name and content
$balise=strtolower($name);
$contenu="";
// we add it to the stack of
array_push($balises,$balise);
// is it a simulation?
if($balise=="simulation"){
// simulation attributes are noted
$impots[simulations][]=array($attributs[MARIE],$attributs[ENFANTS],$attributs[SALAIRE],$attributs[IMPOT]);
}//if
}//startElement
// -----------------------------------------------------------
// the function called when an end tag is encountered
function endElement($parser,$name){
// retrieve the current tag
global $impots,$balises,$contenu;
$balise=array_pop($balises);
// is this an error tag?
if($balise=="erreur"){
// one more mistake
$impots[erreurs][]=trim($contenu);
}//if
}//endElement
// -----------------------------------------------------------
// the tag content processing function
function getData($parser,$data){
// global data
global $balise,$contenu;
// is this an error tag?
if($balise=="erreur"){
// is added to the content of the current tag
$contenu.=$data;
}//if
}//getData
Kommentare:
- Die Funktion getInfos($document) erstellt zunächst einen Parser, konfiguriert diesen anschließend und beginnt schließlich mit der Analyse des $document:
<?php
...
// create an xml text analysis object
$xml_parser=xml_parser_create();
// indicate which functions to execute at the beginning and end of the tag
xml_set_element_handler($xml_parser,"startElement","endElement");
// indicates which function to execute when data is encountered
xml_set_character_data_handler($xml_parser,"getData");
// we analyze $document
xml_parse($xml_parser,$document);
- Geben Sie nach dem Parsen die dem Parser zugewiesenen Ressourcen frei und geben Sie das $imports-Wörterbuch zurück.
<?php
...
// free up resources occupied by the xml parser
xml_parser_free($xml_parser);
// end
return $impots;
- Die Funktion startElement($parser, $name, $attributes) wird am Anfang jedes Tags aufgerufen. Sie
- fügt das Tag $name zu einem Tag-Array $tags hinzu. Dieses Array wird als Stapel verwaltet: Wenn das Symbol für das Tag-Ende auftritt, wird das zuletzt in $tags eingefügte Tag entfernt. Das aktuelle Tag wird zudem in $tag gespeichert. Das $attributes-Wörterbuch enthält die Attribute des gefundenen Tags, wobei diese Attribute in Großbuchstaben geschrieben sind.
- speichert die Attribute im globalen Wörterbuch $impots[simulations], wenn es sich um ein Simulations-Tag handelt.
- Die Funktion getData($parser,$data) wird aufgerufen, wenn der Inhalt $data eines Tags abgerufen wurde. Hier wurde eine Vorsichtsmaßnahme getroffen. Bei bestimmten APIs zur Verarbeitung von XML-Dokumenten, insbesondere in Java, ist zu beachten, dass diese Funktion wiederholt aufgerufen werden kann, d. h., der Inhalt $data eines Tags ist nicht unbedingt auf einmal verfügbar. Die PHP-Dokumentation erwähnt diese Einschränkung nicht. Als Vorsichtsmaßnahme speichern wir den erhaltenen $data-Wert in einer globalen Variablen $content. Erst wenn wir auf das End-of-Tag-Symbol stoßen, gehen wir davon aus, dass wir den gesamten Inhalt des Tags erhalten haben. Das einzige Tag, das von dieser Verarbeitung betroffen ist, ist das <error>-Tag.
- Die Funktion endElement($parser, $name) wird am Ende jedes Tags aufgerufen. Sie wird hier verwendet, um den Namen des aktuellen Tags zu ändern, indem das letzte Tag aus dem Tag-Stack entfernt wird, und um den Inhalt des schließenden <error>-Tags dem Array $impots[errors] hinzuzufügen.
Hier sind einige Ausführungsbeispiele, zunächst mit einem DBMS, das noch nicht gestartet wurde:
dos>e:\php43\php.exe cltXmlSimulations.php http://localhost/poly/impots/8/xmlsimulations.php oui 2 200000
Jeton de session=[e8c29ea12f79e4771960068d161229fd]
Les erreurs suivantes se sont produites :
Impossible d'ouvrir la base DSN [mysql-dbimpots] (S1000)
Anschließend, während das DBMS läuft:
dos>e:\php43\php.exe cltXmlSimulations.php http://localhost/poly/impots/8/xmlsimulations.php oui 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 oui 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 non 2 200000 69a54d79db10b70ed0a2d55d5026ac8b
Jeton de session=[69a54d79db10b70ed0a2d55d5026ac8b]
Simulations :
[oui,3,200000,16400]
[oui,2,200000,22504]
[non,2,200000,33388]
5.5. Fazit
Dank der XML-Antwort ist die Steueranwendung sowohl für den Entwickler als auch für die Entwickler von Client-Anwendungen einfacher zu verwalten.
- Die Gestaltung der Serveranwendung kann nun zwei Personengruppen anvertraut werden: dem PHP-Entwickler des Servlets und dem Grafikdesigner, der das Erscheinungsbild der Browser-Antwort in den Browsern gestaltet. Letzterer muss lediglich die Struktur der XML-Antwort des Servers kennen, um die dazugehörigen Stylesheets zu erstellen. Beachten Sie, dass diese in separaten XSL-Dateien enthalten sind, die unabhängig von der PHP-Anwendung sind. Der Frontend-Designer kann daher unabhängig vom Entwickler arbeiten.
- Auch die Entwickler von Client-Anwendungen müssen lediglich die Struktur der XML-Antwort des Servers kennen. Etwaige Änderungen, die der Frontend-Designer an den Stylesheets vornimmt, haben keinen Einfluss auf diese XML-Antwort, die stets unverändert bleibt. Dies ist ein enormer Vorteil.
- Wie kann der Entwickler seine PHP-Anwendung aktualisieren, ohne alles kaputt zu machen? Zunächst einmal kann er seine Anwendung beliebig organisieren, solange die XML-Antwort unverändert bleibt. Er kann die XML-Antwort auch aktualisieren, solange er die von seinen Clients erwarteten Elemente <error> und <simulation> beibehält. Er kann dieser Antwort somit neue Tags hinzufügen. Der Frontend-Entwickler wird diese in seinen Stylesheets berücksichtigen, und Browser können die aktualisierte Version der Antwort anzeigen. Programmatische Clients hingegen werden weiterhin nach dem alten Modell funktionieren, wobei die neuen Tags einfach ignoriert werden. Damit dies funktioniert, müssen die gesuchten Tags bei der XML-Analyse der Serverantwort eindeutig identifiziert werden. Genau das wurde in unserem XML-Client für die Steueranwendung getan, wo in den Prozeduren ausdrücklich festgelegt wurde, dass wir die Tags <error> und <simulation> verarbeiten. Infolgedessen werden die anderen Tags ignoriert.