Programmation PHP/Exemples/DomXml

Un livre de Wikilivres.



Les documents au format XML ont des imbrications parfois complexes et il n'est pas rare de devoir avoir recours à plusieurs fonctions pour faire le travail de décapsulation du contenu.

À travers cet exemple pratique nous écrirons une fonction pour convertir tout document XML en tableau suivant l'approche du web2 qui tient en deux tags très galvaudés [meta] et [data].

Objectif[modifier | modifier le wikicode]

  • Élaborer une fonction permettant de convertir en tableaux tout XML bien formé.
  • Créer une classe utilitaire domxml pour la recevoir avec ses petites sœurs.

Écriture d'un XML complexe[modifier | modifier le wikicode]

  • Écriture d'un document XML valide à imbrications multiples d'éléments hétéroclites comportant des attributs ou non...
<?xml version="1.0" encoding="UTF-8"?>
<root>
  <tag1 m1="attTag1" m2="att2">
    <sous-tag1>texte sous-tag1
      <sous-tag2></sous-tag2>
      texte tag1
    </sous-tag1>
  </tag1>
  <tag1>
    <sous-tag1>texte sous-tag1
      <sous-tag2 att1="att2" att3="att3"></sous-tag2>
    </sous-tag1>
    <tag3>
      <sous-tag1>texte sous-tag1
        <sous-tag2 att1="attribut1"></sous-tag2>
      </sous-tag1>
      <etEncoreUnTagSuperflu>
        <sous-tag1>texte sous-tag1
          <sous-tag2>test</sous-tag2>
        </sous-tag1>
        <tag1>
          <p><b><sous-tag1>texte sous-tag1
            <sous-tag2 att1="attribut1" /></b>
          </sous-tag1>
          ceci</p>est du body texte à extraire
        </tag1>
      </etEncoreUnTagSuperflu>
      text de début ou de fin
    </tag3>
  </tag1>
  <tagNfo id="1" description="description">texteDescription</tagNfo>
</root>
  • Sauvegarde de ce document.xml bien formé dans le même répertoire.

Création de la fonction[modifier | modifier le wikicode]

On doit maintenant écrire une fonction, la plus optimale possible, pour charger document.xml dans un tableau...

Cette fonction doit :

  • recevoir en entrée un flux xml/rss valide
  • doit migrer les attributs et le contenu dans un tableau

On écrit la fonction récursive qui décapsulera chaque tag en deux sous-tableaux par tag ([meta] ou attributs ) et ([data] ou nœud texte)

Cette fonction doit :

  • tester le type de nœud (texte ou tag)
  • ? si tag >extraire tous ses attributs dans >[meta]
  • ? si texte >extraire le texte dans >[data]
  • comme la structure est imbriquée et non listée :
    • les tags de débuts et de fins ne se suivent pas...
    • la fonction sera donc récursive et s'appellera elle-même pour un output lifo. Elle devra donc se passer son propre résultat en paramètre
    • par soucis du détail technique on fera une fonction getAttribute() pour optimiser le code
   function getAttribute($node)
   {// >((dom)node) ((array)tab)>

     $tab=array();
     foreach($node->attributes() as $k1->$v1)
     {
	$tab[$k1->{''}->name]=$k1->{''}->value;
     }

   return $tab;
   }//

Description :

  • Pour chaque attribut, on place le contenu à une clé du tableau tab à retourner.

On s'attaque ensuite au plus gros du travail de notre convertisseur à savoir domxml2array() :

  function domxml2array($node,&$tab,&$i)
  {// >((dom)node, (array)tab, (int)i) ((array)tab)>
  
    if($next=$node->first_child())        #1
    {
      do
      {
	switch($next->node_type()) #2
        {
	 case 3:
	 $tab['data']=$next->node_value();
	 break;
          case 1:
	 $tab[$next->node_name()."#".++$i]['meta'] = $this->getAttribute($next);
	 $this->domxml2array($next,$tab[$next->node_name()."#".$i],$i);
	 break;
        }
      }while($next=$next->next_sibling()); #3
    }
  return $tab;
  }//

Description :

  1. si le premier enfant existe,
  2. on test le type de nœud,
  3. on passe au nœud suivant.

La fonction utilitaire print_r_html disponible sur php.net permettra de déposer le contenu à l'écran :

function print_r_html($data,$return_data=false)
{
    $data = print_r($data,true);
    $data = str_replace( " ","&nbsp;", $data);
    $data = str_replace( "\r\n","<br/>\r\n", $data);
    $data = str_replace( "\r","<br/>\r", $data);
    $data = str_replace( "\n","<br/>\n", $data);

    if (!$return_data)
        echo $data;
    else
        return $data;
}

Création de la classe[modifier | modifier le wikicode]

On élabore une classe utilitaire pour php4 à implémenter au fur et à mesure :

  • On la baptise DomTree.
  • On y implémente les fonctions créées...
  • On sauvegarde la classe dans DomTree.Class.php.
<?php

Class DomTree
{

#init
  
  var $tab = array();
  var $domNode;

#constructor 

  function DomTree($xml,$type)
  {// >((string)xml,(int)type) ((dom)node)>
  
    if(!$type)
    {
      $this->domNode = domxml_open_file($xml);
    } else {
      $this->domNode = domxml_open_mem($xml);
    }
  }

#get 

  function getTag($tag)
  {// >((string)tag) ((dom)node)>
			
  return $this->domNode->get_elements_by_tagname($tag);
  }//

  function getAttribute($node)
  {// >((dom)node) ((dom)node)>

    $tab=array();
    foreach($node->attributes() as $k1->$v1)
    {
      $tab[$k1->{''}->name]=$k1->{''}->value;
    }

  return $tab;
  }//

#set

#spec

  function domxml2array($node,&$tab,&$i)
  {// >((dom)node, (array)tab, (int)i) ((array)tab)>
  
    if($next=$node->first_child())
    {
      do
      {
	switch($next->node_type()) 
        {
	 case 3:
	 $tab['data']=$next->node_value(); 
	 break;
          case 1:
	 $tab[$next->node_name()."#".++$i]['meta'] = $this->getAttribute($next);
	 $this->domxml2array($next,$tab[$next->node_name()."#".$i],$i);
	 break;
        }
      }while($next=$next->next_sibling());
    }
  return $this->tab=$tab;
  }//

}

?>

Application[modifier | modifier le wikicode]

Dans un fichier test.php on instancie la classe et on l'exécute:

<?php

  header("Cache-Control: no-cache, must-revalidate");
  header("Content-Type: text/html");
  
  // appel de la classe
  require_once"DomTree.class.php";

  // création de l'objet
  $doc  = new DomTree('document.xml');

  // sélection du nœud
  $root = $doc->getTag('root');
  
  // conversion du nœud root en tableau
  $tab = $doc->domxml2array($root[0]);

  // affichage du tableau
  print_r_html($tab);

?>

Aperçu[modifier | modifier le wikicode]

On obtient un arbre structuré easy2use pour le web2

Array
(
    [tag1#1] => Array
        (
            [meta] => Array
                (
                    [m1] => attTag1
                    [m2] => att2
                )

            [sous-tag1#2] => Array
                (
                    [meta] => Array
                        (
                        )

                    [data] => texte tag1
                    [sous-tag2#3] => Array
                        (
                            [meta] => Array
                                (
                                )

                        )

                )

            [data] => 
 
        )

    [data] => 
 
    [tag1#4] => Array
        (
            [meta] => Array
                (
                )

            [sous-tag1#5] => Array
                (
                    [meta] => Array
                        (
                        )

                    [data] => texte sous-tag1
                    [sous-tag2#6] => Array
                        (
                            [meta] => Array
                                (
                                    [att1] => att2
                                    [att3] => att3
                                )

                        )

                )

            [data] => 
 
            [tag3#7] => Array
                (
                    [meta] => Array
                        (
                        )

                    [sous-tag1#8] => Array
                        (
                            [meta] => Array
                                (
                                )

                            [data] => texte sous-tag1
                            [sous-tag2#9] => Array
                                (
                                    [meta] => Array
                                        (
                                        )

                                )

                        )

                    [data] => 
   text de début ou de fin
   
                    [etEncoreUnTagSuperflu#10] => Array
                        (
                            [meta] => Array
                                (
                                )

                            [sous-tag1#11] => Array
                                (
                                    [meta] => Array
                                        (
                                        )

                                    [data] => texte sous-tag1
                                    [sous-tag2#12] => Array
                                        (
                                            [meta] => Array
                                                (
                                                )

                                            [data] => test
                                        )

                                )

                            [data] => 
     
                            [tag1#13] => Array
                                (
                                    [meta] => Array
                                        (
                                        )

                                    [p#14] => Array
                                        (
                                            [meta] => Array
                                                (
                                                )

                                            [b#15] => Array
                                                (
                                                    [meta] => Array
                                                        (
                                                        )

                                                    [sous-tag1#16] => Array
                                                        (
                                                            [meta] => Array
                                                                (
                                                                )

                                                            [data] => texte sous-tag1
                                                            [sous-tag2#17] => Array
                                                                (
                                                                    [meta] => Array
                                                                        (
                                                                            [att1] => attribut1
                                                                        )

                                                                )

                                                        )

                                                )

                                            [data] => 
         ceci
                                        )

                                    [data] => est du body texte à extraire
       
                                )

                        )

                )

        )

    [tagNfo#18] => Array
        (
            [meta] => Array
                (
                    [id] => 1
                    [description] => description
                )

            [data] => texteDescription
        )

)

En bref[modifier | modifier le wikicode]

On a une fonction fort utile à porter sur php5 ou à optimiser histoire de ne plus avoir d'incréments dans les données du tableau.