Programmation PHP/Exemples/DomXml



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

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

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

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

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

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.