5. XML en PHP
In dit hoofdstuk geven we een inleiding tot het gebruik van XML-documenten (eXtensible Markup Language) in combinatie met PHP. We doen dit in de context van de belastingtoepassing die in het vorige hoofdstuk is besproken.
5.1. XML-bestanden en XSL-stylesheets
Laten we eens kijken naar het volgende bestand XML, dat het resultaat van simulaties zou kunnen weergeven:
<?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>
Als we dit weergeven met IE 6, krijgen we het volgende resultaat:

IE6 herkent dat het te maken heeft met een XML-bestand (dankzij de .xml-extensie van het bestand) en zet het op zijn eigen manier op de pagina. Met Netscape krijg je een lege pagina. Als je echter de broncode bekijkt (View/Source), zie je wel degelijk het oorspronkelijke bestand XML:

Waarom geeft Netscape niets weer? Omdat het een stylesheet nodig heeft die aangeeft hoe het bestand XML moet worden omgezet in het bestand HTML, dat het vervolgens kan weergeven. Het blijkt dat IE 6 wel een standaardstylesheet heeft, terwijl het bestand XML er geen heeft, wat hier het geval was.
Er bestaat een taal genaamd XSL (eXtensible StyleSheet Language) waarmee de transformaties kunnen worden beschreven die nodig zijn om een XML-bestand om te zetten in een willekeurig tekstbestand. XSL maakt het gebruik van talrijke instructies mogelijk en lijkt sterk op programmeertalen. We zullen hier niet in detail op ingaan, omdat dat tientallen pagina’s in beslag zou nemen. We zullen slechts twee voorbeelden van XSL-stylesheets beschrijven. De eerste is de stylesheet die het XML-bestand omzet in simulations.xml-code. We passen dit laatste bestand aan zodat het verwijst naar het stylesheet dat browsers kunnen gebruiken om het om te zetten in het document HTML, dat ze vervolgens kunnen weergeven:
<?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>
De opdracht XML
wijst het bestand simulations.xsl aan als een stylesheet (xml-stylesheet) van het type text/xsl c.a.d. een tekstbestand dat code bevat XSL. Dit stylesheet wordt door browsers gebruikt om de tekst XML om te zetten in een document HTML. Hieronder ziet u het resultaat dat wordt verkregen met Netscape 7 bij het laden van het bestand XML simulations.xml:

Wanneer we de broncode van het document bekijken (View/Source), zien we het oorspronkelijke document XML en niet het weergegeven document HTML:

Netscape heeft het stylesheet simulations.xsl gebruikt om het bovenstaande document XML om te zetten in het weergegeven document HTML. Het is nu tijd om de inhoud van dit stylesheet te bekijken:
<?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>
- Een stylesheet XSL is een XML-bestand en volgt dus de regels daarvan. Het moet onder andere "goed gevormd" zijn, dat wil zeggen dat elke geopende tag gesloten moet worden.
- Het bestand begint met twee commando’s XML die in elk stylesheet XSL onder Windows kunnen worden behouden:
<?xml version="1.0" encoding="windows-1252"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Met het attribuut encoding="windows-1252" kun je tekens met accenten in het stylesheet gebruiken.
- De tag <xsl:output method="html" indent="yes"/> geeft aan de XSL-interpreter door dat men "ingesprongen" HTML wil genereren.
- De tag <xsl:template match="element"> wordt gebruikt om het element in het XML-document te definiëren waarop de instructies tussen <xsl:template ...> en </xsl:template> van toepassing zijn.
In het bovenstaande voorbeeld duidt het element "/" de root van het document aan. Dit betekent dat zodra het begin van het document XML wordt aangetroffen, de opdrachten XSL die zich tussen de twee tags bevinden, worden uitgevoerd.
- Alles wat geen XSL-tag is, wordt ongewijzigd in de uitvoerstroom geplaatst. De XSL-tags worden uitgevoerd. Sommige daarvan produceren een resultaat in de uitvoerstroom. Laten we het volgende voorbeeld eens bekijken:
<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>
Ter herinnering: het geanalyseerde document XML is het volgende:
<?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>
Vanaf het begin van het geanalyseerde document XML (match="/") zal de interpreter XSL de volgende tekst genereren
<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>
Merk op dat in de oorspronkelijke tekst <hr/> stond en niet <hr>. In de oorspronkelijke tekst kon men niet <hr> schrijven, want hoewel dit een geldige HTML-tag is, is het een ongeldige XML-tag. We hebben hier echter te maken met een XML-tekst die „goed gevormd“ moet zijn, c.a.d, wat betekent dat elke tag gesloten moet zijn. We schrijven dus <hr/> en omdat we <xsl:output text="html ..."> hebben geschreven, zal de interpreter de tekst <hr/> omzetten in <hr>. Achter deze tekst volgt vervolgens de tekst die wordt gegenereerd door het commando XSL:
We zullen later zien wat deze tekst inhoudt. Ten slotte voegt de interpreter de tekst toe:
De opdracht <xsl:apply-templates select="/simulations/simulation"/> geeft aan dat de "template" (sjabloon) van het element /simulations/simulation moet worden uitgevoerd. Deze wordt uitgevoerd telkens wanneer de XSL-interpreter in de geanalyseerde XML-tekst een <simulation>..</simulations>- of <simulation/>-tag tegenkomt binnen een <simulations>..</simulations>-tag. Zodra een dergelijke tag wordt aangetroffen, voert de interpreter de instructies van het volgende sjabloon uit:
<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>
Laten we de volgende regels XML eens bekijken:
De regel <simulation ..> komt overeen met het sjabloon van de instructie XSL <xsl:apply-templates select="/simulations/simulation>". De interpreter XSL zal dus proberen de instructies die bij dit sjabloon horen, erop toe te passen. Hij zal het sjabloon <xsl:template match="simulation"> vinden en dit uitvoeren. Ter herinnering: wat geen XSL-opdracht is, wordt door de XSL-interpreter ongewijzigd overgenomen, terwijl de XSL-opdrachten worden vervangen door het resultaat van hun uitvoering. De instructie XSL <xsl:value-of select="@champ"/> wordt dus vervangen door de waarde van het attribuut "champ" van het geanalyseerde knooppunt (in dit geval een <simulation>-knooppunt). De analyse van de voorgaande regel XML levert het volgende resultaat op:
XSL | uitvoer |
<tr><td> | <tr><td> |
<xsl:value-of select="@marie"/> | ja |
</td><td> | </td><td> |
<xsl:value-of select="@kinderen"/> | 2 |
</td><td> | </td><td> |
<xsl:value-of select="@salaris"/> | 200000 |
</td><td> | </td><td> |
<xsl:value-of select="@belasting"/> | 22504 |
</td></tr> | </td></tr> |
In totaal de regel XML
wordt omgezet in de regel HTML:
Al deze uitleg is nogal beknopt, maar het zou de lezer nu duidelijk moeten zijn dat de volgende tekst 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>
vergezeld van het volgende stylesheet 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>
genereert de volgende tekst: 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>
Het volgende bestand: 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>
wordt door een recente browser (hier Netscape 7) als volgt weergegeven:

5.2. Belastingapplicatie: versie 5
5.2.1. De bestanden XML en stylesheets XSL van de belastingapplicatie
Laten we teruggaan naar ons uitgangspunt, namelijk de webapplicatie voor belastingen, en onthouden dat we deze willen aanpassen zodat het antwoord aan klanten een antwoord in het formaat XML is in plaats van een antwoord in het formaat HTML. Dit antwoord HTML gaat vergezeld van een stylesheet XSL, zodat browsers het kunnen weergeven. In de vorige paragraaf hebben we het volgende gepresenteerd:
- het bestand simulations.xml, dat het prototype is van een antwoord XML met simulaties van belastingberekeningen
- het bestand simulations.xsl, dat het stylesheet XSL zal vormen dat bij dit antwoord XML hoort
We moeten ook rekening houden met het geval waarin het antwoord fouten bevat. Het prototype van het antwoord XML is in dit geval het volgende bestand 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>
Het stylesheet erreurs.xsl waarmee dit document XML in een browser kan worden weergegeven, ziet er als volgt uit:
<?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>
Dit stylesheet introduceert een nog niet eerder aangetroffen XSL-opdracht: <xsl:value-of select="."/>. Deze opdracht genereert de waarde van het geanalyseerde knooppunt, in dit geval een <erreur>texte</erreur>-knooppunt. De waarde van dit knooppunt is de tekst tussen de twee openings- en sluitingstags, in dit geval texte.
De code erreurs.xml wordt door het stylesheet erreurs.xsl omgezet in het volgende document 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>
Het bestand erreurs.xml wordt samen met het bijbehorende stylesheet als volgt weergegeven in een browser:

5.2.2. De applicatie xmlsimulations
We maken een bestand xmlsimulations.html aan en plaatsen dit in de map van de applicatie impots. De weergegeven pagina ziet er als volgt uit:

Dit document HTML is een statisch document. De code ervan is als volgt:
<html>
<head>
<title>impots</title>
<script language="JavaScript" type="text/javascript">
function effacer(){
// formulier wissen
with(document.frmImpots){
optMarie[0].checked=false;
optMarie[1].checked=true;
txtEnfants.value="";
txtSalaire.value="";
txtImpots.value="";
}//met
}//wissen
function calculer(){
// controle van de instellingen voordat ze naar de server worden verzonden
with(document.frmImpots){
//aantal kinderen
champs=/^\s*(\d+)\s*$/.exec(txtEnfants.value);
if(champs==null){
// het formulier is niet gecontroleerd
alert("Le nombre d'enfants n'a pas été donné ou est incorrect");
nbEnfants.focus();
return;
}//if
//salaris
champs=/^\s*(\d+)\s*$/.exec(txtSalaire.value);
if(champs==null){
// het model is niet gecontroleerd
alert("Le salaire n'a pas été donné ou est incorrect");
salaire.focus();
return;
}//als
// oké – verzenden
submit();
}//met
}//berekenen
</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>
Opgemerkt moet worden dat de gegevens van het formulier worden verzonden naar URL /poly/impots/7/xmlsimulations.php. De code van de toepassing xmlsimulations.php lijkt sterk op die van de toepassing impots.php. De lezer wordt verzocht deze nog eens door te nemen. Ter herinnering: de invoercode is:
<?php
// verwerkt het belastingformulier
// bibliotheken
include "ImpotsDSN.php";
// sessie starten
session_start();
// configuratie van de applicatie
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);
// de sessieparameters worden opgehaald
$session=$_SESSION["session"];
// is de sessie geldig?
if(! isset($session) || ! isset($session[objImpots]) || ! isset($session[simulations])){
// een nieuwe sessie starten
$session=array(objImpots=>new ImpotsDSN($bdImpots),simulations=>array());
// Fouten?
if(count($session[objImpots]->erreurs)!=0){
$requête=array(erreurs=>$session[objImpots]->erreurs);
// foutpagina weergeven
include $erreursImpots;
// einde
$session=array();
terminerSession($session);
}//if
}//if
// de parameters van de lopende uitwisseling worden opgehaald
$requête[marié]=$_POST["optMarie"];
$requête[enfants]=$_POST["txtEnfants"];
$requête[salaire]=$_POST["txtSalaire"];
// zijn alle parameters aanwezig?
if(! isset($requête[marié]) || ! isset($requête[enfants]) || ! isset($requête[salaire])){
// leeg formulier weergeven
$requête=array(chkoui=>"",chknon=>"checked",enfants=>"",salaire=>"",impots=>"",
erreurs=>array(),simulations=>array());
include $formulaireImpots;
// einde
terminerSession($session);
}//if
// parameters controleren
$requête=vérifier($requête);
// fouten?
if(count($requête[erreurs])!=0){
// formulier weergeven
include "$formulaireImpots";
// einde
terminerSession($session);
}//if
// berekening van de verschuldigde belasting
$requête[impots]=$session[objImpots]->calculer(array(marié=>$requête[marié],
enfants=>$requête[enfants],salaire=>$requête[salaire]));
// nog een simulatie
$session[simulations][]=array($requête[marié],$requête[enfants],$requête[salaire],$requête[impots]);
$requête[simulations]=$session[simulations];
// formulier weergeven
include "$formulaireImpots";
// einde
terminerSession($session);
...
De pagina's HTML werden weergegeven via de include-regels "...". We willen hier XML genereren en niet HTML. Het volstaat om twee nieuwe applicaties te schrijven, impots_erreurs.php en impots_simulations.php, zodat deze XML genereren in plaats van HTML. De rest van de applicatie blijft ongewijzigd. De code ziet er dan als volgt uit:
<?php
// verwerkt het belastingformulier
// bibliotheken
include "ImpotsDSN.php";
// sessie starten
session_start();
// configuratie van de applicatie
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);
// de sessieparameters worden opgehaald
$session=$_SESSION["session"];
// is de sessie geldig?
if(! isset($session) || ! isset($session[objImpots]) || ! isset($session[simulations])){
// een nieuwe sessie starten
$session=array(objImpots=>new ImpotsDSN($bdImpots),simulations=>array());
// Fouten?
if(count($session[objImpots]->erreurs)!=0){
$requête=array(erreurs=>$session[objImpots]->erreurs);
// foutpagina weergeven in het formaat XML
header("Content-type: text/xml");
include $erreursImpots;
// einde
$session=array();
terminerSession($session);
}//if
}//if
// de parameters van de lopende uitwisseling worden opgehaald
$requête[marié]=$_POST["optMarie"];
$requête[enfants]=$_POST["txtEnfants"];
$requête[salaire]=$_POST["txtSalaire"];
// zijn alle parameters aanwezig?
if(! isset($requête[marié]) || ! isset($requête[enfants]) || ! isset($requête[salaire])){
// leeg formulier weergeven
include $formulaireImpots;
// einde
terminerSession($session);
}//if
// parameters controleren
$requête=vérifier($requête);
// fouten?
if(count($requête[erreurs])!=0){
// fouten weergeven in XML-formaat
header("Content-type: text/xml");
include "$erreursImpots";
// einde
terminerSession($session);
}//if
// berekening van de verschuldigde belasting
$requête[impots]=$session[objImpots]->calculer(array(marié=>$requête[marié],
enfants=>$requête[enfants],salaire=>$requête[salaire]));
// nog een simulatie
$session[simulations][]=array($requête[marié],$requête[enfants],$requête[salaire],$requête[impots]);
$requête[simulations]=$session[simulations];
// weergave van simulaties in het formaat XML
header("Content-type: text/xml");
include "$simulationsImpots";
// einde
terminerSession($session);
We hebben eerder de twee soorten antwoorden XML die moeten worden verstrekt, evenals de bijbehorende stylesheets, gepresenteerd en bestudeerd. De code van de toepassing impots_simulations.php is als volgt:
<?php
// genereert de code XML van de simulatiepagina van de belastingapplicatie
// enkele constanten
$xslSimulations="simulations.xsl";
// kopteksten XML
echo "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
echo "<?xml-stylesheet type=\"text/xsl\" href=\"$xslSimulations\" ?>\n";
// de simulaties
echo "<simulations>\n";
for ($i=0;$i<count($requête[simulations]);$i++){
// simulatie $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";
?>
Met deze code kan op basis van het woordenboek $requête code XML worden gegenereerd die vergelijkbaar is met het volgende:
<?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>
Het stylesheet simulations.xsl zal deze code XML omzetten in de code HTML.
De code van de applicatie impots_erreurs.php is als volgt:
<?php
// genereert de code XML van de foutpagina van de applicatie impots
// enkele constanten
$xslErreurs="erreurs.xsl";
// kopteksten XML
echo "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
echo "<?xml-stylesheet type=\"text/xsl\" href=\"$xslErreurs\" ?>\n";
// de fouten
echo "<erreurs>\n";
for ($i=0;$i<count($requête[erreurs]);$i++){
// fout $i
echo "<erreur>".$requête[erreurs][$i]."</erreur>";
}//for
echo "</erreurs>\n";
?>
Met deze code kan op basis van het woordenboek $requête code XML worden gegenereerd die vergelijkbaar is met het volgende:
<?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>
Het stylesheet erreurs.xsl zal deze code XML omzetten in code HTML.
Laten we een eerste voorbeeld bekijken:

De SGBD MySQL wordt niet gestart. We krijgen dan het volgende antwoord:

Als we de broncode bekijken die door de browser is ontvangen, zien we het volgende:
<?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>
Nu starten we de SGBD en MySQL en voeren we verschillende opeenvolgende simulaties uit. We krijgen het volgende antwoord:

De code die de browser ontvangt, is als volgt:
<?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>
Het valt op dat onze nieuwe applicatie eenvoudiger te onderhouden is dan de vorige. Een deel van het werk is overgeheveld naar de stylesheets XSL. Het voordeel van deze nieuwe taakverdeling is dat, zodra het formaat XML van de antwoorden is vastgesteld, de ontwikkeling van de stylesheets losstaat van die van de applicatie.
5.3. Analyse van een XML-document in PHP
De volgende versie van onze belastingapplicatie wordt een client die is geprogrammeerd op basis van de vorige applicatie xmlsimulations. Onze client zal dus XML-code ontvangen die hij moet analyseren om de informatie eruit te halen die voor hem van belang is. We zullen hier even pauzeren in onze verschillende versies en leren hoe we een document XML in PHP kunnen analyseren. We doen dit aan de hand van het volgende voorbeeld:
dos>e:\php43\php.exe xmlParser.php
syntaxe : xmlParser.php fichierXML
De toepassing xmlParser.php accepteert één parameter: de URI (Uniform Resource Identifier) van het te analyseren document XML. In ons voorbeeld is deze URI simpelweg de naam van een XML-bestand dat zich in de map van de applicatie xmlParser.php bevindt. Laten we twee uitvoervoorbeelden bekijken. In het eerste voorbeeld is het geanalyseerde bestand XML het volgende bestand 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>
De analyse levert de volgende resultaten op:
dos>e:\php43\php.exe xmlParser.php erreurs.xml
ERREURS
ERREUR
[erreur 1]
/ERREUR
ERREUR
[erreur 2]
/ERREUR
/ERREURS
We hadden nog niet beschreven wat de applicatie xmlParser.php doet, maar hier zien we dat deze de structuur van het geanalyseerde document XML weergeeft. Het tweede voorbeeld analyseert het volgende bestand: 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>
De analyse levert de volgende resultaten op:
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
De applicatie xmlParser.php bevat alles wat we nodig hebben in onze belastingapplicatie, aangezien deze zowel de fouten als de simulaties heeft kunnen opsporen die de webserver zou kunnen versturen. Laten we de code eens bekijken:
<?php
// syntaxis $0 fichierXML
// toont de structuur en inhoud van het bestand fichierXML
// controle van de aanroep
if(count($argv)!=2){
// foutmelding
fwrite(STDERR,"syntaxe : $argv[0] fichierXML\n");
// einde
exit(1);
}//if
// initialisaties
$file=$argv[1]; // het XML-bestand
$depth=0; // inspringingsniveau = diepte in de boomstructuur
// het programma
// er wordt een XML-tekstanalyseobject aangemaakt
$xml_parser=xml_parser_create();
// we geven aan welke functies bij het begin en einde van een tag moeten worden uitgevoerd
xml_set_element_handler($xml_parser,"startElement","endElement");
// we geven aan welke functie moet worden uitgevoerd wanneer gegevens worden aangetroffen
xml_set_character_data_handler($xml_parser,"afficheData");
// het XML-bestand wordt geopend in leesmodus
if (! ($fp=@fopen($file,"r"))){
fwrite(STDERR,"impossible d'ouvrir le fichier xml $file\n");
exit(2);
}//if
// verwerking van het XML-bestand in blokken van 4096 bytes
while($data=fread($fp,4096)){
// analyse van de ingelezen gegevens
if (! xml_parse($xml_parser,$data,feof($fp))){
// er is een fout opgetreden
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));
// einde
exit(3);
}//if
}//while
// het bestand is doorzocht
// de door de XML-parser bezette bronnen worden vrijgegeven
xml_parser_free($xml_parser);
// einde
exit(0);
// -----------------------------------------------------------
// functie die wordt aangeroepen bij het tegenkomen van een starttag
function startElement($parser,$name,$attributs){
global $depth;
// een reeks spaties (inspringing)
for($i=0;$i<$depth;$i++){
print " ";
}//for
// attributen
$précisions="";
while(list($attrib,$valeur)=each($attributs)){
$précisions.="($attrib,$valeur) ";
}
// de naam van de tag en eventuele attributen worden weergegeven
if($précisions)
print "$name,$précisions\n";
else print "$name\n";
// een extra niveau in de boomstructuur
$depth++;
}//startElement
// -----------------------------------------------------------
// de functie die wordt aangeroepen bij het tegenkomen van een afsluitende tag
function endElement($parser,$name){
// eindtag
// inspringingsniveau
global $depth;
$depth--;
// een reeks spaties (inspringing)
for($i=0;$i<$depth;$i++){
echo " ";
}//for
// de naam van de tag
echo "/$name\n";
}//endElement
// -----------------------------------------------------------
// de functie voor het weergeven van gegevens
function afficheData($parser,$data){
// inspringingsniveau
global $depth;
// de gegevens worden weergegeven
$data=trim($data);
if($data!=""){
// een reeks spaties (inspringing)
for($i=0;$i<$depth;$i++){
echo " ";
}//for
echo "[$data]\n";
}//if
}//afficheData
?>
Laten we de code met betrekking tot XML eens bekijken. Om een document XML te analyseren, heeft onze applicatie een XML-code-analysator nodig, die gewoonlijk een "parser" wordt genoemd.
Wanneer de parser het document XML gaat analyseren, genereert hij gebeurtenissen zoals: ik ben het begin van het document tegengekomen, het begin van een tag, een tagattribuut, de inhoud van een tag, het einde van een tag, het einde van het document, ... Hij stuurt deze gebeurtenissen door naar methoden die we aan hem moeten toewijzen:
<?php
...
// we geven aan welke functies aan het begin en einde van de tag moeten worden uitgevoerd
xml_set_element_handler($xml_parser,"startElement","endElement");
// we geven aan welke functie moet worden uitgevoerd wanneer er gegevens worden aangetroffen
xml_set_character_data_handler($xml_parser,"afficheData");
gebeurtenis gegenereerd door de parser | verwerkingsmethode |
functie startElement($parser, $name, $attributen) $parser: de parser van het document $name: naam van het geanalyseerde element. Als het aangetroffen element <simulations> is, is name="simulations". $attributs: lijst met attributen van de tag in de vorm (ATTRIBUT,waarde), waarbij ATTRIBUT de naam van de tag in hoofdletters is. | |
functie afficheData($parser,$data) $parser: de parser van het document $data: de gegevens van de tag | |
functie endElement($parser, $name) de parameters zijn dezelfde als die van de methode startElement. |
Met de functie startElement kunnen de attributen van het element worden opgehaald via de parameter $attributs. Dit is een woordenboek met de attributen van de tag. Stel dat we de volgende tag hebben:
dan ziet het woordenboek $attributs er als volgt uit: array(marie=>ja,kinderen=>2,salaris=>200000,belasting=>22504)
Zodra de parser en de voorgaande methoden zijn gedefinieerd, wordt de analyse van een document uitgevoerd door de functie xml_parse:
<?php
...
// het XML-bestand wordt verwerkt in blokken van 4096 bytes
while($data=fread($fp,4096)){
// analyse van de gelezen gegevens
if (! xml_parse($xml_parser,$data,feof($fp))){
...
}//if
}//while
function xml_parse($parser, $doc, $fin) De parser $parser analyseert het document $doc. $doc kan een deel zijn van een groter document. De parameter $fin geeft aan of dit het laatste deel is (true) of niet (false). Bij het analyseren van het document $doc worden de functies die zijn gedefinieerd door xml_set_element_handler aangeroepen bij elke begin- en eindtag. Wordt de functie die is gedefinieerd door xml_set_character_data_handler elke keer aangeroepen wanneer de inhoud van een tag is opgehaald? |
Tijdens de analyse van het document XML kunnen er fouten optreden, met name als het document XML „onjuist is opgebouwd”, bijvoorbeeld wanneer sluitingstags ontbreken. In dat geval retourneert de functie xml_parse een waarde die als „onwaar“ wordt geïnterpreteerd:
<?php
...
if (! xml_parse($xml_parser,$data,feof($fp))){
// er is een fout opgetreden
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));
// einde
exit(3);
}//if
functie xml_get_error_code($parser) geeft de code van de laatst opgetreden fout terug -- functie xml_error_string($code) geeft de foutmelding terug die bij de als parameter doorgegeven code hoort -- functie xml_get_current_line($parser) geeft het regelnummer van het document XML dat momenteel door de parser wordt geanalyseerd |
Zodra het document is geparseerd, worden de aan de parser toegewezen bronnen vrijgegeven:
function xml_free($parser) |
Nu dit is uitgelegd, spreekt het vorige programma, samen met de uitvoervoorbeelden, voor zich.
5.4. Belastingapplicatie: versie 6
We beschikken nu over alle elementen om een clientprogramma te schrijven voor onze belastingdienst dat XML genereert. We gebruiken versie 4 van onze applicatie voor de client en behouden versie 5 voor de server. In deze client-server-applicatie:
- wordt de simulatie van de belastingberekening uitgevoerd door de applicatie xmlsimulations.php. Het antwoord van de server is dus in het formaat XML, zoals we in versie 5 hebben gezien.
- is de client niet langer een browser, maar een zelfstandige PHP-client. De console-interface is die van versie 4.
De lezer wordt verzocht de code van de applicatie cltImpots.php nog eens door te nemen; dit was de geprogrammeerde client van versie 4. Deze ontving een document $document van de server. Dit was toen een document HTML. Het is nu een document XML. Het document HTML $document werd geanalyseerd door de volgende functie:
<?php
...
function getInfos($document){
// $document: document HTML
// er wordt gezocht naar ofwel de foutenlijst
// of de tabel met simulaties
// voorbereiding van het resultaat
$impots[erreurs]=array();
$impots[simulations]=array();
........
return $impots;
}//getInfos
De functie ontving het document HTML $document, analyseerde het en leverde een woordenboek $impots op met twee attributen:
- fouten: een array met fouten
- simulaties: een array met simulaties, waarbij elke simulatie zelf een array met vier elementen is (echtgenote, kinderen, salaris, belasting).
De toepassing cltImpots.php wordt nu cltXmlSimulations.php. Alleen het gedeelte dat het van de server ontvangen document verwerkt, moet worden aangepast om rekening te houden met het feit dat het nu een XML-document is. De functie getInfos ziet er dan als volgt uit:
<?php
...
// --------------------------------------------------------------
function getInfos($document){
// $document: document XML
// we zoeken ofwel de foutenlijst
// of de tabel met simulaties
global $impots,$balises;
// voorbereiding van het resultaat
$impots[erreurs]=array();
$impots[simulations]=array();
// lopende tags
$balises=array();
// er wordt een XML-tekstanalyseobject aangemaakt
$xml_parser=xml_parser_create();
// we geven aan welke functies aan het begin en einde van de tag moeten worden uitgevoerd
xml_set_element_handler($xml_parser,"startElement","endElement");
// we geven aan welke functie moet worden uitgevoerd wanneer gegevens worden aangetroffen
xml_set_character_data_handler($xml_parser,"getData");
// $document wordt geanalyseerd
xml_parse($xml_parser,$document);
// de door de XML-parser bezette bronnen worden vrijgegeven
xml_parser_free($xml_parser);
// einde
return $impots;
}//getInfos
// -----------------------------------------------------------
// functie die wordt aangeroepen bij het tegenkomen van een starttag
function startElement($parser,$name,$attributs){
global $impots,$balise,$balises,$contenu;
// de naam van de tag en de inhoud ervan worden genoteerd
$balise=strtolower($name);
$contenu="";
// deze wordt toegevoegd aan de tag-stack
array_push($balises,$balise);
// is dit een simulatie?
if($balise=="simulation"){
// de attributen van de simulatie worden genoteerd
$impots[simulations][]=array($attributs[MARIE],$attributs[ENFANTS],$attributs[SALAIRE],$attributs[IMPOT]);
}//if
}//startElement
// -----------------------------------------------------------
// de functie die wordt aangeroepen bij het tegenkomen van een eindtag
function endElement($parser,$name){
// de huidige tag wordt opgehaald
global $impots,$balises,$contenu;
$balise=array_pop($balises);
// is dit een fouttag?
if($balise=="erreur"){
// nog een fout
$impots[erreurs][]=trim($contenu);
}//if
}//endElement
// -----------------------------------------------------------
// de functie voor het verwerken van de inhoud van een tag
function getData($parser,$data){
// globale gegevens
global $balise,$contenu;
// is dit een fouttag?
if($balise=="erreur"){
// wordt toegevoegd aan de inhoud van de huidige tag
$contenu.=$data;
}//if
}//getData
Opmerkingen:
- de functie getInfos($document) begint met het aanmaken van een parser, configureert deze vervolgens en start ten slotte de analyse van het document $document:
<?php
...
// er wordt een XML-tekstanalyseobject aangemaakt
$xml_parser=xml_parser_create();
// we geven aan welke functies aan het begin en einde van de tag moeten worden uitgevoerd
xml_set_element_handler($xml_parser,"startElement","endElement");
// we geven aan welke functie moet worden uitgevoerd wanneer gegevens worden aangetroffen
xml_set_character_data_handler($xml_parser,"getData");
// we voeren de analyse uit van $document
xml_parse($xml_parser,$document);
- Na afloop van de analyse worden de aan de parser toegewezen bronnen vrijgegeven en wordt het woordenboek $impots teruggegeven.
<?php
...
// de door de XML-parser bezette bronnen worden vrijgegeven
xml_parser_free($xml_parser);
// einde
return $impots;
- de functie startElement($parser,$name,$attributs) wordt bij elke tag-start aangeroepen. Ze
- merkt de tag $name op in een tabel met tags $balises. Deze tabel wordt beheerd als een stapel: zodra het symbool voor het einde van de tag wordt aangetroffen, wordt de laatste tag die in $balises is gestapeld, van de stapel verwijderd. De huidige tag wordt ook genoteerd in $balise. In het woordenboek $attributs staan de attributen van de aangetroffen tag, waarbij deze attributen in hoofdletters zijn weergegeven.
- slaat de attributen op in het globale woordenboek $impots[simulations], als het om een simulatietag gaat.
- de functie getData($parser,$data) wanneer de inhoud $data van een tag is opgehaald. Hier is een voorzorgsmaatregel genomen. In bepaalde API, met name Java, voor de verwerking van XML-documenten, wordt aangegeven dat deze functie herhaaldelijk kan worden aangeroepen, c.a.d. dat de inhoud van een tag niet noodzakelijkerwijs in één keer beschikbaar is. Hier vermeldt de documentatie PHP deze beperking niet. Uit voorzorg wordt de verkregen waarde $data opgeslagen in een globale variabele $contenu. Pas wanneer het symbool voor het einde van de tag wordt aangetroffen, wordt aangenomen dat de volledige inhoud van de tag is verkregen. De enige tag waarop deze verwerking van toepassing is, is de tag <erreur>.
- De functie endElement($parser,$name) wordt bij elk einde van een tag aangeroepen. Deze functie wordt hier gebruikt om de naam van de huidige tag te wijzigen door de laatste tag uit de tagstapel te verwijderen en om de inhoud van de tag <erreur>, die eindigt bij de array $impots[erreurs], toe te voegen.
Hier volgen enkele uitvoervoorbeelden, allereerst met een SGBD die niet is gestart:
dos>e:\php43\php.exe cltXmlSimulations.php http://localhost/poly/impots/8/xmlsimulations.php ja 2 200000
Jeton de session=[e8c29ea12f79e4771960068d161229fd]
Les erreurs suivantes se sont produites :
Impossible d'ouvrir la base DSN [mysql-dbimpots] (S1000)
Vervolgens met de wel gestarte SGBD:
dos>e:\php43\php.exe cltXmlSimulations.php http://localhost/poly/impots/8/xmlsimulations.php ja 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 ja 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 nee 2 200000 69a54d79db10b70ed0a2d55d5026ac8b
Jeton de session=[69a54d79db10b70ed0a2d55d5026ac8b]
Simulations :
[oui,3,200000,16400]
[oui,2,200000,22504]
[non,2,200000,33388]
5.5. Conclusie
Dankzij de XML-oplossing is de belastingapplicatie eenvoudiger te beheren geworden, zowel voor de ontwikkelaar ervan als voor de ontwikkelaars van de clientapplicaties.
- Het ontwerp van de serverapplicatie kan nu worden toevertrouwd aan twee soorten mensen: de PHP-ontwikkelaar van de servlet en de grafisch ontwerper die het uiterlijk van de browserrespons in de browsers zal beheren. Deze laatste hoeft alleen de structuur van het XML-antwoord van de server te kennen om de bijbehorende stylesheets op te stellen. Ter herinnering: deze worden opgeslagen in aparte XSL-bestanden die onafhankelijk zijn van de PHP-applicatie. De grafisch ontwerper kan dus onafhankelijk van de ontwikkelaar werken.
- Ook de ontwerpers van de clienttoepassingen hoeven alleen maar de structuur van het XML-antwoord van de server te kennen. De wijzigingen die de grafisch ontwerper in de stylesheets aanbrengt, hebben geen invloed op dit XML-antwoord, dat altijd hetzelfde blijft. Dit is een enorm voordeel.
- Hoe kan de ontwikkelaar zijn applicatie PHP verder ontwikkelen zonder alles kapot te maken? Ten eerste kan hij, zolang zijn antwoord XML niet verandert, zijn applicatie naar eigen inzicht inrichten. Hij kan ook het antwoord XML aanpassen, zolang hij de elementen <fout> en <simulatie> behoudt die zijn klanten verwachten. Zo kan hij nieuwe tags aan dit antwoord toevoegen. De grafisch ontwerper houdt hier rekening mee in zijn stylesheets en browsers kunnen de nieuwe versies van het antwoord ontvangen. De geprogrammeerde clients blijven echter werken met het oude model, waarbij de nieuwe tags simpelweg worden genegeerd. Om dit mogelijk te maken, moeten de gezochte tags in de analyse XML van het serverantwoord correct worden geïdentificeerd. Dit is gedaan in onze client XML van de belastingapplicatie, waar in de procedures specifiek werd aangegeven dat de tags <erreur> en <simulation> werden verwerkt. Daardoor worden de andere tags genegeerd.