Skip to content

5. XML e PHP

In questo capitolo forniremo un'introduzione all'utilizzo dei documenti XML (eXtensible Markup Language) con PHP. Lo faremo nel contesto dell'applicazione fiscale esaminata nel capitolo precedente.

5.1. File XML e fogli di stile XSL

Consideriamo il seguente file XML, che potrebbe rappresentare il risultato di alcune simulazioni:

<?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>

Se lo si visualizza con IE 6, si ottiene il seguente risultato:

Image

IE6 riconosce che si tratta di un file XML (grazie al suffisso .xml del file) e lo impagina a modo suo. Con Netscape si ottiene una pagina vuota. Tuttavia, se si osserva il codice sorgente (View/Source), si nota che è presente il file originale XML:

Image

Perché Netscape non visualizza nulla? Perché ha bisogno di un foglio di stile che gli indichi come trasformare il file XML nel file HTML, che potrà quindi visualizzare. Si dà il caso che IE 6 disponga di un foglio di stile predefinito, mentre il file XML non ne propone alcuno, come nel caso in questione.

Esiste un linguaggio chiamato XSL (eXtensible StyleSheet Language) che permette di descrivere le trasformazioni da effettuare per convertire un file XML in un file di testo qualsiasi. XSL consente l’uso di numerose istruzioni e assomiglia molto ai linguaggi di programmazione. Non lo descriveremo in dettaglio qui, poiché ci vorrebbero diverse decine di pagine. Ci limiteremo semplicemente a descrivere due esempi di fogli di stile XSL. Il primo è quello che trasformerà il file XML simulations.xml in codice HTML. Modifichiamo quest’ultimo in modo che indichi il foglio di stile che i browser potranno utilizzare per trasformarlo nel documento HTML che potranno visualizzare:

<?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>

L'ordine XML

<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>

indica il file simulations.xsl come foglio di stile (xml-stylesheet) di tipo text/xsl c.a.d. un file di testo contenente codice XSL. Questo foglio di stile verrà utilizzato dai browser per trasformare il testo XML in un documento HTML. Ecco il risultato ottenuto con Netscape 7 quando si carica il file XML simulations.xml:

Image

Quando si esamina il codice sorgente del documento (Visualizza/Sorgente), si ritrova il documento XML iniziale e non il documento HTML visualizzato:

Image

Netscape ha utilizzato il foglio di stile simulations.xsl per trasformare il documento XML sopra riportato nel documento HTML visualizzabile. È ora il momento di esaminare il contenuto di questo foglio di stile:

<?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>
  • Un foglio di stile XSL è un file XML e ne segue quindi le regole. Deve essere, tra le altre cose, "ben formato", ovvero ogni tag aperto deve essere chiuso.
  • Il file inizia con due comandi XML che è possibile mantenere in qualsiasi foglio di stile XSL su Windows:
<?xml version="1.0" encoding="windows-1252"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

L'attributo encoding="windows-1252" consente di utilizzare i caratteri accentati nel foglio di stile.

  • Il tag <xsl:output method="html" indent="yes"/> indica all'interprete XSL che si desidera generare un HTML "indentato".
  • Il tag <xsl:template match="elemento"> serve a definire l'elemento del documento XML a cui si applicheranno le istruzioni contenute tra <xsl:template ...> e </xsl:template>.
    <xsl:template match="/">
................
        </xsl:template>

Nell'esempio sopra riportato, l'elemento "/" indica la radice del documento. Ciò significa che non appena verrà individuato l'inizio del documento XML, verranno eseguiti i comandi XSL situati tra i due tag.

  • Tutto ciò che non è un tag XSL viene inserito tal quale nel flusso di output. I tag XSL vengono invece eseguiti. Alcuni di essi producono un risultato nel flusso di output. Esaminiamo il seguente esempio:
    <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>

Ricordiamo che il documento XML analizzato è il seguente:

<?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>

Fin dall'inizio del documento XML analizzato (match="/"), l'interprete XSL produrrà in uscita il testo

<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>

Si noti che nel testo iniziale era presente <hr/> e non <hr>. Nel testo iniziale non era possibile scrivere <hr>, che, pur essendo un tag HTML valido, è un tag XML non valido. Ora, in questo caso abbiamo a che fare con un testo XML che deve essere "ben formato", c.a.d il che significa che ogni tag deve essere chiuso. Si scrive quindi <hr/> e, poiché è stato scritto <xsl:output text="html ...">, l’interprete trasformerà il testo <hr/> in <hr>. A seguito di questo testo, seguirà il testo generato dal comando XSL:

      <xsl:apply-templates select="/simulations/simulation"/>

Vedremo in seguito di quale testo si tratti. Infine, l’interprete aggiungerà il testo:

                    </table>
          </center>
        </body>
      </html>

Il comando <xsl:apply-templates select="/simulations/simulation"/> richiede l'esecuzione del "template" (modello) dell'elemento /simulations/simulation. Verrà eseguito ogni volta che l'interprete XSL incontrerà nel testo XML analizzato un tag <simulation>..</simulations> o <simulation/> all'interno di un tag <simulations>..</simulations>. All’individuazione di tale tag, l’interprete eseguirà le istruzioni del modello seguente:

    <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>

Prendiamo le seguenti righe XML:

<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>

La riga <simulation ..> corrisponde al modello dell'istruzione XSL <xsl:apply-templates select="/simulations/simulation>". L'interprete XSL cercherà quindi di applicarvi le istruzioni che corrispondono a questo modello. Troverà il modello <xsl:template match="simulation"> e lo eseguirà. Ricordiamo che ciò che non è un comando XSL viene ripreso tal quale dall’interprete XSL, mentre i comandi XSL vengono sostituiti dal risultato della loro esecuzione. L’istruzione XSL <xsl:value-of select="@champ"/> viene quindi sostituita dal valore dell’attributo "champ" del nodo analizzato (in questo caso un nodo <simulation>). L’analisi della riga precedente XML produrrà in uscita il seguente risultato:

XSL
output
<tr><td>
<tr><td>
<xsl:value-of select="@marie"/>
</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="@imposta"/>
22504
</td></tr>
</td></tr>

In totale, la riga XML

<simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>

verrà trasformata nella riga HTML:

<tr><td>oui</td><td>2</td><td>200000</td><td>22504</td></tr>

Tutte queste spiegazioni sono un po’ rudimentali, ma ora dovrebbe risultare chiaro al lettore che il testo XML seguente:

<?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>

accompagnato dal seguente foglio di stile 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>

genera il seguente testo 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>

Il file XML simulations.xml seguente

<?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>

visualizzato da un browser recente (in questo caso Netscape 7) appare quindi come segue:

Image

5.2. Applicazione fiscale: versione 5

5.2.1. I file XML e i fogli di stile XSL dell’applicazione fiscale

Torniamo al nostro punto di partenza, ovvero l’applicazione web per le imposte, e ricordiamo che vogliamo modificarla in modo che la risposta fornita ai clienti sia in formato XML anziché HTML. Questa risposta HTML sarà accompagnata da un foglio di stile XSL affinché i browser possano visualizzarla. Nel paragrafo precedente abbiamo presentato:

  • il file simulations.xml, che è il prototipo di una risposta XML contenente simulazioni di calcolo delle imposte
  • il file simulations.xsl, che costituirà il foglio di stile XSL che accompagnerà questa risposta XML

Dobbiamo prevedere anche il caso di una risposta contenente errori. Il prototipo della risposta XML in questo caso sarà il seguente file 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>

Il foglio di stile erreurs.xsl che consente di visualizzare questo documento XML in un browser sarà il seguente:

<?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>    

Questo foglio di stile introduce un comando XSL non ancora incontrato: <xsl:value-of select="."/>. Questo comando produce in uscita il valore del nodo analizzato, in questo caso un nodo <erreur>texte</erreur>. Il valore di questo nodo è il testo compreso tra i due tag di apertura e chiusura, in questo caso texte.

Il codice erreurs.xml viene trasformato dal foglio di stile erreurs.xsl nel seguente documento 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>

Il file erreurs.xml, insieme al relativo foglio di stile, viene visualizzato dal browser nel modo seguente:

Image

5.2.2. L'applicazione xmlsimulations

Creiamo un file xmlsimulations.html che inseriamo nella directory dell'applicazione impots. La pagina visualizzata è la seguente:

Image

Questo documento HTML è un documento statico. Il suo codice è il seguente:

<html>
    <head>
      <title>impots</title>
    <script language="JavaScript" type="text/javascript">
        function effacer(){
           // azzeramento del modulo
        with(document.frmImpots){
            optMarie[0].checked=false;
          optMarie[1].checked=true;
          txtEnfants.value="";
          txtSalaire.value="";
          txtImpots.value="";
        }//con
      }//cancella

      function calculer(){
           // verifica dei parametri prima di inviarli al server
        with(document.frmImpots){
          //numero di figli
          champs=/^\s*(\d+)\s*$/.exec(txtEnfants.value);
          if(champs==null){
            // il modello non è stato verificato
            alert("Le nombre d'enfants n'a pas été donné ou est incorrect");
            nbEnfants.focus();
            return;
          }//if
           //stipendio
          champs=/^\s*(\d+)\s*$/.exec(txtSalaire.value);
          if(champs==null){
            // il modello non è stato verificato
            alert("Le salaire n'a pas été donné ou est incorrect");
            salaire.focus();
            return;
          }//se
          // Va bene - inviamo
          submit();
        }//con
      }//calcolare        
        </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>

Si noti che i dati del modulo vengono inviati a URL /poly/impots/7/xmlsimulations.php. Il codice dell'applicazione xmlsimulations.php è molto simile a quello dell'applicazione impots.php. Si invita il lettore a consultare quest'ultimo. Ricordiamo il codice di ingresso:

<?php
     // elabora il modulo fiscale

     // librerie
  include "ImpotsDSN.php";

   // avvio sessione
  session_start();

   // configurazione dell'applicazione
  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);

     // recupera i parametri della sessione
  $session=$_SESSION["session"];
   // sessione valida?
  if(! isset($session) || ! isset($session[objImpots]) || ! isset($session[simulations])){
       // si avvia una nuova sessione
    $session=array(objImpots=>new ImpotsDSN($bdImpots),simulations=>array());
    // Ci sono errori?
    if(count($session[objImpots]->erreurs)!=0){
      $requête=array(erreurs=>$session[objImpots]->erreurs);
         // visualizzazione della pagina degli errori
        include $erreursImpots;
       // fine
      $session=array();
      terminerSession($session);
    }//if
  }//if

  // si recuperano i parametri dello scambio in corso
  $requête[marié]=$_POST["optMarie"];
  $requête[enfants]=$_POST["txtEnfants"];
  $requête[salaire]=$_POST["txtSalaire"];

   // Sono presenti tutti i parametri?
  if(! isset($requête[marié]) || ! isset($requête[enfants]) || ! isset($requête[salaire])){
       // visualizzazione del modulo vuoto
    $requête=array(chkoui=>"",chknon=>"checked",enfants=>"",salaire=>"",impots=>"",
            erreurs=>array(),simulations=>array());    
    include $formulaireImpots;
     // fine
    terminerSession($session);
  }//if

   // verifica dei parametri
  $requête=vérifier($requête);

   // Ci sono errori?
  if(count($requête[erreurs])!=0){
       // visualizzazione del modulo
    include "$formulaireImpots";
     // fine
    terminerSession($session);
  }//se

   // calcolo dell'imposta dovuta
  $requête[impots]=$session[objImpots]->calculer(array(marié=>$requête[marié],
        enfants=>$requête[enfants],salaire=>$requête[salaire]));

   // un'altra simulazione
  $session[simulations][]=array($requête[marié],$requête[enfants],$requête[salaire],$requête[impots]);
  $requête[simulations]=$session[simulations];

   // visualizzazione del modulo
  include "$formulaireImpots";

   // fine
  terminerSession($session);
...

Le pagine HTML venivano visualizzate tramite le righe include "...". In questo caso si desidera generare XML e non HTML. È sufficiente scrivere due nuove applicazioni, impots_erreurs.php e impots_simulations.php, affinché generino XML invece di HTML. Il resto dell’applicazione rimane invariato. Il codice diventa quindi il seguente:

<?php
     // elabora il modulo fiscale

     // librerie
  include "ImpotsDSN.php";

   // avvio della sessione
  session_start();

   // configurazione dell'applicazione
  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);

     // recupera i parametri della sessione
  $session=$_SESSION["session"];
   // sessione valida?
  if(! isset($session) || ! isset($session[objImpots]) || ! isset($session[simulations])){
       // si avvia una nuova sessione
    $session=array(objImpots=>new ImpotsDSN($bdImpots),simulations=>array());
    // Errori?
    if(count($session[objImpots]->erreurs)!=0){
      $requête=array(erreurs=>$session[objImpots]->erreurs);
         // visualizzazione della pagina degli errori nel formato XML
          header("Content-type: text/xml");      
        include $erreursImpots;
       // fine
      $session=array();
      terminerSession($session);
    }//if
  }//if

   // si recuperano i parametri dello scambio in corso
  $requête[marié]=$_POST["optMarie"];
  $requête[enfants]=$_POST["txtEnfants"];
  $requête[salaire]=$_POST["txtSalaire"];

   // si dispone di tutti i parametri?
  if(! isset($requête[marié]) || ! isset($requête[enfants]) || ! isset($requête[salaire])){
       // visualizzazione del modulo vuoto
    include $formulaireImpots;
     // fine
    terminerSession($session);
  }//if

   // verifica dei parametri
  $requête=vérifier($requête);

   // Ci sono errori?
  if(count($requête[erreurs])!=0){
       // visualizzazione errori in formato XML
      header("Content-type: text/xml");    
    include "$erreursImpots";
     // fine
    terminerSession($session);
  }//se

   // calcolo dell'imposta dovuta
  $requête[impots]=$session[objImpots]->calculer(array(marié=>$requête[marié],
        enfants=>$requête[enfants],salaire=>$requête[salaire]));

   // un'altra simulazione
  $session[simulations][]=array($requête[marié],$requête[enfants],$requête[salaire],$requête[impots]);
  $requête[simulations]=$session[simulations];

   // visualizzazione delle simulazioni in formato XML
  header("Content-type: text/xml");
  include "$simulationsImpots";

   // fine
  terminerSession($session);

Abbiamo già presentato e analizzato i due tipi di risposta XML da fornire, nonché i fogli di stile che devono accompagnarli. Il codice dell'applicazione impots_simulations.php è il seguente:

<?php
     // genera il codice XML della pagina delle simulazioni dell'applicazione «imposti»

   // alcune costanti
  $xslSimulations="simulations.xsl";

   // intestazioni XML
  echo "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
  echo "<?xml-stylesheet type=\"text/xsl\" href=\"$xslSimulations\" ?>\n";    
  // le simulazioni
  echo "<simulations>\n";
  for ($i=0;$i<count($requête[simulations]);$i++){
      // simulazione $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";
  }//per
  echo "</simulations>\n";
?>

Questo codice consente, a partire dal dizionario $requête, di generare un codice XML analogo al seguente:


<?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>

Il foglio di stile simulations.xsl trasformerà questo codice XML nel codice HTML.

Il codice dell'applicazione impots_erreurs.php è il seguente:

<?php
     // genera il codice XML della pagina degli errori dell'applicazione impots

   // alcune costanti
  $xslErreurs="erreurs.xsl";

   // intestazioni XML
  echo "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
  echo "<?xml-stylesheet type=\"text/xsl\" href=\"$xslErreurs\" ?>\n";
   // gli errori
  echo "<erreurs>\n";
  for ($i=0;$i<count($requête[erreurs]);$i++){
      // errore $i
    echo "<erreur>".$requête[erreurs][$i]."</erreur>";
  }//for
  echo "</erreurs>\n";
?>

Questo codice consente, a partire dal dizionario $requête, di generare un codice XML analogo al seguente:

<?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>

Il foglio di stile erreurs.xsl trasformerà questo codice XML nel codice HTML.

Vediamo un primo esempio:

Image

Il SGBD MySQL non viene eseguito. Si riceve quindi la seguente risposta:

Image

Se si osserva il codice sorgente ricevuto dal browser, si nota quanto segue:

<?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>

Ora avviamo SGBD e MySQL ed eseguiamo diverse simulazioni successive. Otteniamo la seguente risposta:

Image

Il codice ricevuto dal browser è il seguente:

<?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>

Si noti che la nostra nuova applicazione è più semplice da mantenere rispetto alla precedente. Parte del lavoro è stata trasferita ai fogli di stile XSL. Il vantaggio di questa nuova ripartizione dei compiti è che, una volta definito il formato XML delle risposte, lo sviluppo dei fogli di stile è indipendente da quello dell’applicazione.

5.3. Analisi di un documento XML in PHP

La prossima versione della nostra applicazione fiscale sarà un client programmato sulla base della precedente applicazione xmlsimulations. Il nostro client riceverà quindi il codice XML, che dovrà analizzare per estrarne le informazioni di suo interesse. A questo punto faremo una pausa nelle nostre diverse versioni per imparare come analizzare un documento XML in PHP. Lo faremo partendo dal seguente esempio:


dos>e:\php43\php.exe xmlParser.php
syntaxe : xmlParser.php fichierXML

L’applicazione xmlParser.php accetta un parametro: l’URI (Uniform Resource Identifier) del documento XML da analizzare. Nel nostro esempio, questo URI sarà semplicemente il nome di un file XML situato nella directory dell'applicazione xmlParser.php. Consideriamo due esempi di esecuzione. Nel primo esempio, il file XML analizzato è il seguente file 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>

L'analisi fornisce i seguenti risultati:


dos>e:\php43\php.exe xmlParser.php erreurs.xml
ERREURS
 ERREUR
  [erreur 1]
 /ERREUR
 ERREUR
  [erreur 2]
 /ERREUR
/ERREURS

Non avevamo ancora spiegato a cosa servisse l'applicazione xmlParser.php, ma qui si vede che visualizza la struttura del documento XML analizzato. Il secondo esempio analizza il seguente file 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>

L'analisi fornisce i seguenti risultati:


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

L'applicazione xmlParser.php contiene tutto ciò di cui abbiamo bisogno nella nostra applicazione fiscale, poiché è stata in grado di individuare sia gli errori che le simulazioni che il server web avrebbe potuto inviare. Esaminiamo il suo codice:

<?php
   // sintassi $0 fichierXML
   // visualizza la struttura e il contenuto del file fichierXML

   // verifica chiamata
  if(count($argv)!=2){
       // messaggio di errore
    fwrite(STDERR,"syntaxe : $argv[0] fichierXML\n");
     // fine
    exit(1);
  }//if

   // inizializzazioni
  $file=$argv[1];       // il file xml
  $depth=0;               // livello di indentazione = profondità nella struttura ad albero

   // il programma

   // si crea un oggetto di analisi del testo XML
  $xml_parser=xml_parser_create();

   // si specificano le funzioni da eseguire all'inizio e alla fine del tag
  xml_set_element_handler($xml_parser,"startElement","endElement");

   // si indica quale funzione eseguire quando si incontrano i dati
  xml_set_character_data_handler($xml_parser,"afficheData");

  // apertura del file XML in lettura
  if (! ($fp=@fopen($file,"r"))){
    fwrite(STDERR,"impossible d'ouvrir le fichier xml $file\n");
    exit(2);
  }//if
   // elaborazione del file XML in blocchi da 4096 byte
  while($data=fread($fp,4096)){
    // analisi dei dati letti
    if (! xml_parse($xml_parser,$data,feof($fp))){
      // si è verificato un errore
      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));
       // fine
      exit(3);
    }//if
  }//while
  // il file è stato analizzato
   // si liberano le risorse occupate dall'analizzatore XML
  xml_parser_free($xml_parser);
   // fine
  exit(0);

    // -----------------------------------------------------------  
   // funzione chiamata quando si incontra un tag di apertura
  function startElement($parser,$name,$attributs){
    global $depth;
     // una sequenza di spazi (indentazione)
    for($i=0;$i<$depth;$i++){
      print " ";
    }//for
     // attributi
    $précisions="";
    while(list($attrib,$valeur)=each($attributs)){
      $précisions.="($attrib,$valeur) ";
    }
     // vengono visualizzati il nome del tag e gli eventuali attributi
    if($précisions)
      print "$name,$précisions\n";
    else print "$name\n";
     // un ulteriore livello della struttura ad albero
    $depth++;
  }//startElement

    // -----------------------------------------------------------  
   // la funzione chiamata quando si incontra un tag di chiusura
  function endElement($parser,$name){
     // tag di chiusura
     // livello di indentazione
    global $depth;
    $depth--;
    // una sequenza di spazi (indentazione)
    for($i=0;$i<$depth;$i++){
      echo " ";
    }//for
     // il nome del tag
    echo "/$name\n";
  }//endElement

    // -----------------------------------------------------------  
   // la funzione di visualizzazione dei dati
  function afficheData($parser,$data){
     // livello di indentazione
    global $depth;
     // i dati vengono visualizzati
    $data=trim($data);
    if($data!=""){
       // una sequenza di spazi (rientro)
      for($i=0;$i<$depth;$i++){
        echo " ";
      }//for
      echo "[$data]\n";
    }//se
  }//afficheData
?>

Esaminiamo il codice relativo a XML. Per analizzare un documento XML, la nostra applicazione necessita di un analizzatore di codice XML, comunemente denominato "parser".

<?php
...
   // si crea un oggetto di analisi del testo XML
  $xml_parser=xml_parser_create();

Quando il parser analizzerà il documento XML, genererà eventi quali: ho individuato l’inizio del documento, l’inizio di un tag, un attributo di tag, il contenuto di un tag, la fine di un tag, la fine del documento, ... Trasmette questi eventi ai metodi che gli devono essere indicati:

<?php
...
   // si specificano le funzioni da eseguire all'inizio e alla fine del tag
  xml_set_element_handler($xml_parser,"startElement","endElement");
   // si indica quale funzione eseguire quando si incontrano i dati
  xml_set_character_data_handler($xml_parser,"afficheData");
evento generato dal
parser
metodo di elaborazione
début d'un élément :<balise>
funzione startElement($parser, $name, $attributi)
$parser: il parser del documento
$name: nome dell'elemento analizzato. Se l'elemento individuato è <simulations>, si avrà name="simulations".
$attributs: elenco degli attributi del tag nella forma (ATTRIBUT,valore) dove ATTRIBUT è il nome in maiuscolo del tag.
valeur d'un élément :
<balise>valeur</balise>
funzione afficheData($parser,$data)
$parser: il parser del documento
$data: i dati del tag
fin d'un élément : </balise> ou <balise .../>
funzione endElement($parser, $name)
i parametri sono quelli del metodo startElement.

La funzione startElement consente di recuperare gli attributi dell’elemento tramite il parametro $attributs. Si tratta di un dizionario degli attributi del tag. Pertanto, se si ha il seguente tag:

  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>

il dizionario $attributs sarà il seguente: array(marie=>sì,enfants=>2,salaire=>200000,impot=>22504)

Una volta definiti il parser e i metodi precedenti, l’analisi di un documento avviene tramite la funzione xml_parse:

<?php
...
   // elaborazione del file XML in blocchi da 4096 byte
  while($data=fread($fp,4096)){
    // analisi dei dati letti
    if (! xml_parse($xml_parser,$data,feof($fp))){
...
    }//if
  }//while
analyse d'un
document $doc
funzione xml_parse($parser, $doc, $fin)
Il parser $parser analizza il documento $doc. $doc potrebbe essere una parte di un documento più ampio. Il parametro $fin indica se si tratta dell'ultima parte (true) o meno (false). Durante l'analisi del documento $doc, le funzioni definite da xml_set_element_handler vengono richiamate all'inizio e alla fine di ogni tag. La funzione definita da xml_set_character_data_handler viene invece richiamata ogni volta che viene ottenuto il contenuto di un tag.

Durante l’analisi del documento XML, possono verificarsi degli errori, in particolare se il documento XML è “mal formato”, ad esempio in caso di tag di chiusura mancanti. In questo caso, la funzione xml_parse restituisce un valore pari a “falso”:

<?php
...
    if (! xml_parse($xml_parser,$data,feof($fp))){
      // si è verificato un errore
      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));
      // fine
      exit(3);
    }//if
gestion d'erreurs
funzione xml_get_error_code($parser)
restituisce il codice dell'ultimo errore verificatosi
--
funzione xml_error_string($code)
restituisce il messaggio di errore associato al codice passato come parametro
--
funzione xml_get_current_line($parser)
restituisce il numero della riga del documento XML attualmente in fase di analisi da parte del parser

Una volta completata l'analisi del documento, si liberano le risorse assegnate al parser:

<?php
...
   // si liberano le risorse occupate dall'analizzatore XML
  xml_parser_free($xml_parser);
libération des
ressources attribuées
au parseur $parser
funzione xml_free($parser)

Una volta chiarito questo punto, il programma precedente, corredato dagli esempi di esecuzione, risulta di per sé comprensibile.

5.4. Applicazione fiscale: versione 6

Ora disponiamo di tutti gli elementi necessari per scrivere un client programmato per il nostro servizio fiscale che genera il formato XML. Riprendiamo la versione 4 della nostra applicazione per realizzare il client e manteniamo la versione 5 per quanto riguarda il server. In questa applicazione client-server:

  • il servizio di simulazione del calcolo delle imposte è gestito dall’applicazione xmlsimulations.php. La risposta del server è quindi nel formato XML, come abbiamo visto nella versione 5.
  • il client non è più un browser, ma un client PHP autonomo. La sua interfaccia a riga di comando è quella della versione 4.

Si invita il lettore a rileggere il codice dell’applicazione cltImpots.php, che era il client programmato della versione 4. Quest’ultimo riceveva dal server un documento $document. A quel punto, il documento era un HTML. Ora è un documento XML. Il documento HTML $document veniva analizzato dalla seguente funzione:

<?php
...
  function getInfos($document){
       // $document: documento HTML
     // si cerca o l'elenco degli errori
     // oppure la tabella delle simulazioni

     // preparazione del risultato
    $impots[erreurs]=array();
    $impots[simulations]=array();

........
    return $impots;
    }//getInfos

La funzione riceveva il documento HTML $document, lo analizzava e restituiva un dizionario $impots con due attributi:

  1. errori: un array di errori
  2. simulazioni: un array di simulazioni, dove ogni simulazione è a sua volta un array di quattro elementi (coniuge, figli, stipendio, imposta).

L'applicazione cltImpots.php diventa ora cltXmlSimulations.php. Solo la parte che elabora il documento ricevuto dal server deve essere modificata per tenere conto del fatto che ora si tratta di un documento XML. La funzione getInfos diventa quindi la seguente:

<?php
...
// --------------------------------------------------------------
  function getInfos($document){
       // $document: documento XML
     // si cerca l'elenco degli errori
     // oppure la tabella delle simulazioni

    global $impots,$balises;

     // preparazione del risultato
    $impots[erreurs]=array();
    $impots[simulations]=array();
     // tag in corso
    $balises=array();

     // si crea un oggetto di analisi del testo XML
    $xml_parser=xml_parser_create();
     // si specificano le funzioni da eseguire all'inizio e alla fine del tag
    xml_set_element_handler($xml_parser,"startElement","endElement");
     // si indica quale funzione eseguire quando si incontrano i dati
    xml_set_character_data_handler($xml_parser,"getData");
    // si esegue l'analisi di $document
    xml_parse($xml_parser,$document);
     // si liberano le risorse occupate dall'analizzatore XML
    xml_parser_free($xml_parser);
     // fine
    return $impots;
  }//getInfos

    // -----------------------------------------------------------  
  // funzione chiamata quando si incontra un tag di apertura
  function startElement($parser,$name,$attributs){
    global $impots,$balise,$balises,$contenu;

     // si annotano il nome del tag e il suo contenuto
    $balise=strtolower($name);
    $contenu="";
     // viene aggiunto allo stack dei tag
    array_push($balises,$balise);    
    // si tratta di una simulazione?
    if($balise=="simulation"){   
         // si annotano gli attributi della simulazione
      $impots[simulations][]=array($attributs[MARIE],$attributs[ENFANTS],$attributs[SALAIRE],$attributs[IMPOT]);
    }//if
  }//startElement

    // -----------------------------------------------------------  
   // la funzione chiamata quando si incontra un tag di fine
  function endElement($parser,$name){
       // si recupera il tag corrente
    global $impots,$balises,$contenu;
    $balise=array_pop($balises);
    // si tratta di un tag di errore?
    if($balise=="erreur"){
         // un altro errore
      $impots[erreurs][]=trim($contenu);
    }//if
  }//endElement

    // -----------------------------------------------------------  
   // la funzione di elaborazione del contenuto di un tag
  function getData($parser,$data){
      // dati globali
    global $balise,$contenu;
     // si tratta di un tag di errore?
    if($balise=="erreur"){
         // si aggiunge al contenuto del tag corrente
      $contenu.=$data;
    }//if
  }//getData

Commenti:

  • la funzione getInfos($document) inizia creando un parser, poi lo configura e infine avvia l'analisi del documento $document:
<?php
...
     // si crea un oggetto di analisi del testo XML
    $xml_parser=xml_parser_create();
     // si specificano le funzioni da eseguire all'inizio e alla fine del tag
    xml_set_element_handler($xml_parser,"startElement","endElement");
     // si indica quale funzione eseguire quando si incontrano i dati
    xml_set_character_data_handler($xml_parser,"getData");
    // si esegue l'analisi di $document
    xml_parse($xml_parser,$document);
  • al termine dell'analisi, si liberano le risorse assegnate al parser e si restituisce il dizionario $impots.
<?php
...
     // si liberano le risorse occupate dall'analizzatore XML
    xml_parser_free($xml_parser);
     // fine
    return $impots;
  • la funzione startElement($parser,$name,$attributs) viene chiamata all'inizio di ogni tag. Essa
    • Rileva il tag $name in una tabella di tag $balises. Questa tabella è gestita come uno stack: quando si incontra il simbolo di fine tag, l'ultimo tag impilato in $balises verrà rimossa dallo stack. Il tag corrente viene inoltre annotato in $balise. Nel dizionario $attributs si trovano gli attributi del tag incontrato, scritti in maiuscolo.
    • registra gli attributi nel dizionario globale $impots[simulations], se si tratta di un tag di simulazione.
  • la funzione getData($parser,$data) quando il contenuto $data di un tag è stato recuperato. In questo caso è stata presa una precauzione. In alcuni API, in particolare in Java, relativi all’elaborazione di documenti XML, è indicato che questa funzione può essere richiamata ripetutamente, c.a.d. non si dispone necessariamente del contenuto di un tag in un'unica volta. In questo caso, la documentazione PHP non indica questa restrizione. Per precauzione, si somma il valore $data ottenuto a una variabile globale $contenu. Solo quando si incontra il simbolo di fine tag si riterrà di aver ottenuto l’intero contenuto del tag. L’unico tag interessato da questa operazione è il tag <erreur>.
  • La funzione endElement($parser,$name) viene chiamata alla fine di ogni tag. Viene utilizzata in questo caso per modificare il nome del tag corrente rimuovendo l'ultimo tag dallo stack dei tag e per aggiungere il contenuto del tag <erreur>, che termina con l'array $impots[erreurs].

Ecco alcuni esempi di esecuzione, innanzitutto con un SGBD che non è stato avviato:

dos>e:\php43\php.exe cltXmlSimulations.php http://localhost/poly/impots/8/xmlsimulations.php sì 2 200000
Jeton de session=[e8c29ea12f79e4771960068d161229fd]
Les erreurs suivantes se sont produites :
Impossible d'ouvrir la base DSN [mysql-dbimpots] (S1000)

Poi con il SGBD eseguito:


dos>e:\php43\php.exe cltXmlSimulations.php http://localhost/poly/impots/8/xmlsimulations.php sì 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 sì 2 200000 69a54d79db10b70ed0a2d55d5026ac8b
Jeton de session=[69a54d79db10b70ed0a2d55d5026ac8b]
Simulations :
[oui,3,200000,16400]
[oui,2,200000,22504]

dos >e:\php43\php.exe cltXmlSimulations.php http://localhost/poly/impots/8/xmlsimulations.php no 2 200000 69a54d79db10b70ed0a2d55d5026ac8b
Jeton de session=[69a54d79db10b70ed0a2d55d5026ac8b]
Simulations :
[oui,3,200000,16400]
[oui,2,200000,22504]
[non,2,200000,33388]

5.5. Conclusione

Grazie alla sua risposta XML, l'applicazione fiscale è diventata più facile da gestire sia per il suo progettista che per i progettisti delle applicazioni client.

  • La progettazione dell’applicazione server può ora essere affidata a due figure professionali: lo sviluppatore PHP del servlet e il grafico che si occuperà dell’aspetto della risposta del browser nei vari browser. A quest’ultimo basta conoscere la struttura della risposta XML del server per creare i fogli di stile che la accompagneranno. Ricordiamo che questi ultimi sono contenuti in file XSL separati e indipendenti dall’applicazione PHP. Il grafico può quindi lavorare indipendentemente dallo sviluppatore.
  • Anche i progettisti delle applicazioni client devono semplicemente conoscere la struttura della risposta XML del server. Le modifiche che il grafico potrebbe apportare ai fogli di stile non hanno alcuna ripercussione su questa risposta XML, che rimane sempre la stessa. Si tratta di un enorme vantaggio.
  • Come può lo sviluppatore far evolvere la propria applicazione PHP senza comprometterne il funzionamento? Innanzitutto, fintanto che la sua risposta XML non cambia, può organizzare la propria applicazione come preferisce. Può anche aggiornare la risposta XML purché mantenga gli elementi <errore> e <simulazione> richiesti dai propri clienti. Può quindi aggiungere nuovi tag a questa risposta. Il grafico ne terrà conto nei propri fogli di stile e i browser potranno ricevere le nuove versioni della risposta. I client programmati, invece, continueranno a funzionare con il vecchio modello, ignorando semplicemente i nuovi tag. Affinché ciò sia possibile, è necessario che nell’analisi XML della risposta del server i tag ricercati siano correttamente identificati. È quanto è stato fatto nel nostro client XML dell’applicazione fiscale, dove nelle procedure si specificava espressamente che venivano trattati i tag <erreur> e <simulation>. Di conseguenza, gli altri tag vengono ignorati.