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 | 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 :
- si le premier enfant existe,
- on test le type de nœud,
- 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( " "," ", $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.