[Plan du site]
Vous êtes ici ---
> Le Site du Zér0
> Les tutoriels
> Non-Officiels
> Site Web
> PHP
> Langage, bibliothèques et fonctions
> Lecture du tutoriel
[DOM] Parser du XML - L'exemple du zCode
Vous vous apprêtez à lire un tutoriel rédigé par un membre de ce site. Malgré tout le soin que ce membre a pu apporter au tutoriel, nous ne pouvons pas garantir que les informations contenues sur cette page sont exactes à 100%. Merci de garder cela en tête lorsque vous lirez cette page ;o)
Bonjour à tous, chers Zér0s !
Si vous vous demandez ce qu'est un
parseur (ou bien encore "
parser") XML, à quoi ça sert, ou bien comment élaborer un langage comme le zCode, ce tuto est fait pour vous.
Nous aborderons ensemble les bases du parsage en xml, à travers la mise en place d'un système de parsage pour le zCode ! Mais je vous sens intrigués, pleins de fougue et d'entrain : entrons sans plus tarder dans le vif du sujet.
Pré requis
Avant de mettre les mains dans le cambouis, je tiens à vous avertir que vous devez déjà avoir suivi
en entier le cours de M@teo21 sur le PHP (les expressions régulières comprises), ainsi que ce tuto
qui introduit le langage XML, de tangui.
Un parseur XML...
... qu'est-ce que c'est ?
Si vous avez lu les chapitres concernant les REGEX, vous avez codé
un parseur pour le bbcode. En réalité, un parseur XML fait exactement la même chose, à la différence que le langage à parser est basé sur du XML. Le parseur est chargé de traduire le code XML en (x)HTML. Le parseur est donc un script PHP qui va réaliser cette transformation.
"Parseur" est le mot francisé de "parser" en anglais. On parle aussi d'analyseur syntaxique.
... pourquoi XML ?
En plus des avantages du parsage en général, un parseur XML offre son (gros) lot d'avantages. En effet, grâce à
l'API DOM intégré à PHP5, on peut non seulement élaborer très facilement un parseur, mais aussi gérer des fonctionnalités de langage plus poussées.

Voici une liste non exhaustive des atouts de DOM par rapport aux REGEX :
- plus "subtil"
- plus complet.
- permet de gérer des fichiers XML (sauvegarde, ouverture, fermeture).
La seule contrainte de DOM est de respecter les règles élémentaires du XML (balises entre chevrons, etc).
Dites, ça ne vous fait pas penser à un langage existant, ce genre de système ?

Mais si ! Le
zCode ! Effectivement, le zCode utilise DOM et le XML pour parser ses messages (
consultez cette page pour confirmation). Au final, votre langage ressemblera peut-être au zCode. Nous nous baserons sur ce dernier pour illustrer l'utilisation de DOM.
J'utiliserai des balises spécifiques au zCode. Pour nos visiteurs qui ne seraient pas encore familiers avec le zCode, voici ? encore, me direz-vous ?
un peu de lecture.
DOM n'est disponible que pour PHP5. Son prédécesseur, DOMxml (PHP4), et lui n'ont pas grand-chose en commun, bien que le principe reste le même.
Jetez
un coup d'oeil à cette brève présentation de DOM, puis découvrons ensemble la magie du parsage XML

.
But de cette partie
L'objectif est de vous initier à DOM en parsant une balise "
gras". Ce n'est peut-être pas encore le nirvana, mais il s'agit d'un passage nécessaire, afin que nous puissions dans la prochaine partie réaliser un parseur digne de ce nom.
Vous pouvez intégrer les feuilles de style du SdZ afin d'obtenir un résultat très fidèle au rendu du SdZ.
Chargeons le XML
Comment charger du XML pour l'utiliser dans DOM ? C'est ce que nous allons voir.
La notion d'arbre XML
Ce terme désigne l'arborescence d'un langage XML, c'est-à-dire l'enchaînement de noeuds qui s'imbriquent les uns dans les autres. DOM se sert de cet arbre pour fonctionner : on dit qu'il a un fonctionnement
hiérarchique. Ci-dessous, un exemple d'arbre xml.
Code : XML1
2
3
4
5
6
7
8
9 | <?xml version="1.0" encoding="ISO-8859-1"?>
<zcode>
<gras>Bonjour</gras>, ça va ?
<couleur nom="rouge">Très bien, merci.</couleur>
<liste>
<puce>Et tes enfants ?</puce>
<puce>Ils passent trop de temps sur le SdZ. :)</puce>
</liste>
</zcode>
|
Récupérer du contenu XML
Nous allons voir comment récupérer cet arbre avec DOM. Cette étape est
indispensable.
DOM est orienté objet. Par conséquent, DOM comporte forcément des classes, des méthodes que nous allons utiliser. Nous allons nous familiariser avec ces classes au fur et à mesure, en pratiquant.
On commence par initialiser deux éléments. L'un est le document XML chargé par DOM, le second est la chaîne de caractères qui va contenir le code xHTML à afficher (facultatif pour cette dernière).
Code : PHP1
2
3
4 | <?php
$document_xml = new DomDocument(); // Instanciation de la classe DomDocument : création d'un nouvel objet.
$resultat_html = ''; // Initialisation de la chaîne qui contient le résultat.
?>
|
On voit que le premier type de classe que nous utilisons est
DomDocument : il s'agit d'un document DOM

. Pour l'instant,
$document_xml est vide. Il faut donc le remplir par du XML. On peut le faire de deux manières.
Imaginons que vous ayez un fichier
zcode.xml dans le même dossier que votre script. Il faut l'ouvrir avec DOM. Pour cela, on utilise la méthode
load, c'est une méthode
DomDocument, bien entendu.
Code : PHP1
2
3 | <?php
$document_xml->load('zcode.xml'); // Chargement à partir de zcode.xml
?>
|
Vous remarquez qu'on obtient la méthode
load grâce à "
->" : il en sera de même pour les variables d'un objet.
On peut faire la même chose à partir d'une chaîne de caractères tout à fait banale.
Code : PHP 1
2
3
4
5
6
7
8
9
10
11 | <?php
$chaine_xml = '<?xml version="1.0" encoding="ISO-8859-1"? >
<zcode><gras>Bonjour</gras>, ça va ?
<couleur nom="rouge">Très bien, merci.</couleur>
<liste>
<puce>Et tes enfants ?</puce>
<puce>Ils passent trop de temps sur le SdZ. :)</puce>
</liste>
</zcode>'; // Chaîne de caractères contenant le texte XML à parser.
$document_xml->loadXML($chaine_xml); // On charge du XML à partir de $chaine_xml.
?>
|
Vous pouvez aussi sauvegarder une chaîne de caractères xml dans un fichier.
Code : PHP1
2
3 | <?php
$document_xml->save('un_nouveau_fichier_xml.xml');
?>
|
Comment récupérer des balises ?
Nous avons chargé le xml : comment récupérer les balises afin de les parser ?
La notion de noeud
Un noeud est un
élément de l'arbre XML. Dans notre exemple, on peut distinguer le noeud "
zcode", le noeud "
gras", le noeud "
couleur", le noeud "
liste", le noeud "
nom", etc.
Un noeud peut donc être :
- un ensemble de balises qui vont par paires (<zcode></zcode>)
- un contenu textuel (à l'intérieur de balises < gras></ gras>, par exemple)
- un attribut (son nom)
- la valeur de l'attribut (entre guillemets).
Quand on parle du noeud "
zcode", on parle de tout ce qui se trouve entre
<zcode> et
</zcode> :
Code : XML1
2
3
4
5
6 | <gras>Bonjour</gras>, ça va ?
<couleur nom="rouge">Très bien, merci.</couleur>
<liste>
<puce>Et tes enfants ?</puce>
<puce>Ils passent trop de temps sur le SdZ. :)</puce>
</liste>
|
Le noeud "
gras" contient :
Code : XML
En fait, "gras" contient un noeud textuel appelé "#text", qui lui contient bien uniquement une valeur "Bonjour".
On distingue des catégories de noeuds disctinctes, définies selon leur organisation.
- Noeud racine : c'est celui qui englobe le contenu du fichier XML. Ici, "zcode" est le noeud racine. Il ne peut y avoir qu'un noeud racine par document.
- Noeud parent : on parle du noeud qui englobe le noeud dont on parle. Pour nous, le noeud parent de "puce" est "liste", celui de "liste" est "zcode". Notez bien que "nom" a pour noeud parent "couleur".
- Noeud associé : on utilise ce terme pour désigner les attributs. Le noeud associé de "couleur" est "nom". Par contre, "liste" n'a pas de noeud associé.
- Noeud enfant : on veut parler des noeuds compris dans le noeud dont on parle. Par exemple, le noeud enfant de "liste" est "puce". Remarquez que le noeud enfant de "nom" est "rouge" : cela désigne aussi les valeurs des attributs.
On peut aussi les distinguer selon leur type (noeud textuel, attribut, etc).
DOM : un véritable sac de noeuds
DOM repose
entièrement sur les noeuds, et vous serez inexorablement amenés à les utiliser. DOM "découpe" les noeuds et les distingue selon leur position dans l'arbre XML.
| Nom du noeud | Caractéristiques du noeud | Contenu du noeud |
|---|
| puce (n°2) |
Noeud enfant de liste et de zcode
A pour enfant un noeud "#text" invisible, contenant le vrai texte. |
Code : XML1 | Ils passent trop de temps sur le SdZ. :)
|
|
| puce (n°1) |
Noeud enfant de liste et de zcode
A pour enfant un noeud "#text" invisible, contenant le vrai texte. |
Code : XML |
| liste |
Noeud enfant de zcode
Noeud parent de puce |
Code : XML1
2 | <puce>Et tes enfants ?</puce>
<puce>Ils passent trop de temps sur le SdZ. :)</puce>
|
|
| nom |
Noeud enfant de couleur
A pour enfant un noeud "#text" invisible, contenant le vrai texte. |
Code : XML1 | <police nom="courrier">rouge</police>
|
|
| couleur |
Noeud enfant de zcode
A pour enfant un noeud "#text" invisible, contenant le vrai texte. |
Code : XML |
| Texte (sans noeud particulier) |
Noeud enfant de zcode
A pour enfant un noeud "#text" invisible, contenant le vrai texte. |
Code : XML |
| gras |
Noeud enfant de zcode
A pour enfant un noeud "#text" invisible, contenant le vrai texte. |
Code : XML |
| zcode |
Noeud racine |
Code : XML1
2
3
4
5
6 | <gras>Bonjour</gras>, ça va ?
<couleur nom="rouge">Très bien, merci.</couleur>
<liste>
<puce>Et tes enfants ?</puce>
<puce>Ils passent trop de temps sur le sdz :)</puce>
</liste>
|
|
Vous voyez que DOM décompose l'arbre XML en détaillant beaucoup. Tout ce qu'il nous reste à faire, c'est exploiter cette décomposition intelligemment. On peut également représenter une interprétation DOM sous la forme d'un arbre :
Un document XML selon DOM
Vous comprenez à présent les appellations "arbre" et "noeud" : les programmeurs filent souvent la métaphore.
En pratique
Récupérons le noeud "
zcode".
Code : PHP1
2
3 | <?php
$elements = $document_xml->getElementsByTagName('zcode');
?>
|
Nous utilisons
getElementsByTagName, ce qui signifie
obtenirElementsParNomDeBalise. On récupère ainsi le noeud "
zcode" (il n'y en a qu'un car c'est le noeud racine).
Cette méthode retourne un objet de type
DomNodeList. C'est une classe qui a pour caractéristique de lister des noeuds.
Ce ne sont pas des array ! On ne peut pas obtenir les entrées avec des crochets ! Par contre, on peut se servir de la structure foreach.
Pour récupérer des éléments d'une instance de
DomNodeList, vous devez utiliser la méthode
item. Exemple :
Code : PHP1
2
3 | <?php
$premier_element_liste = $liste_dom->item(0);
?>
|
Les
DomNodeList sont composées d'objets de type
DomNode.
DomNode est une classe qui représente un noeud. Si vous récupériez les noeuds "
puce", par exemple, vous pourriez les obtenir avec
foreach.
Code : PHP1
2
3
4
5
6 | <?php
foreach($elements as $element)
{
// Effectuez les opérations sur $element.
}
?>
|
Mais nous ne ferons pas comme cela, tout bonnement parce qu'avec cette méthode, on parse par nom de balise, mais pas dans l'ordre du texte

. Essayez, vous verrez ! Nous récupérerons donc "
zcode" avec
item, car comme il n'y a qu'un noeud "
zcode" il est forcément le premier (et le seul) élément de la liste.
Code : PHP1
2
3
4 | <?php
$element = $elements->item(0); // On obtient le noeud zcode
$enfants = $element->childNodes; // On récupère les noeuds enfants de zcode avec childNodes
?>
|
ChildNodes est une variable d'instance contenant les enfants de "
zcode" sous forme de liste
DOMNodeList. Le reste coule de source. Nous allons prendre chaque noeud enfant séparément, l'analyser, et le parser en fonction du type de noeud rencontré. C'est machiavélique

. Pour ce faire, je dois ajouter que
DomNode dispose de deux variables qui contiennent respectivement le nom du noeud et son contenu, ce sont
nodeName et
nodeValue. De plus, chaque élément de texte "banal" s'appelle "
#text".
Code : PHP 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | <?php
$element = $elements->item(0); // On obtient le noeud zcode.
$enfants = $element->childNodes; // On récupère les noeuds enfants de zcode avec childNodes.
foreach($enfants as $enfant) // On prend chaque noeud enfant séparément.
{
$nom = $enfant->nodeName; // On prend le nom de chaque noeud.
if ($nom == 'gras')
{
$resultat_html .= '<strong>'.$enfant->nodeValue.'</strong>';
}
elseif($nom == '#text')
{
$resultat_html .= $enfant->nodeValue;
}
else
{
$resultat_html .= $enfant->nodeValue;
}
} ?>
|
Peut-être ne connaissez-vous pas le signe ".=" : il ajoute quelque chose à une variable. Écrire $truc.= 'machin'; c'est comme écrire $truc = $truc . 'machin'; .
Nous avons réalisé quelque chose d'assez archaïque, il faut bien le dire. Mais c'est maintenant que le vrai travail commence !
C'est bien beau de pouvoir mettre en gras votre texte, mais ce n'est pas ça qui va nous avancer. L'exemple précédent a été réalisé pour vous faire assimiler la technique de base. Qu'attendons-nous pour passer à la vitesse supérieure, que diable ?

C'est à partir de maintenant que ça devient très intéressant.
Une véritable stratégie
Décrivons d'abord le principe de base de notre parseur.
Principe de notre parseur xml
Vous saisissez l'idée principale, non ? En fait, ce système permet :
- de parcourir TOUT l'arbre XML, dans l'ordre de lecture, quel que soit le nombre d'enfants ou de ramifications
- de ne parser que des noeuds dont le contenu a déjà été traité, pour éviter "d'oublier" des noeuds.
Ce système se base sur trois fonctions principales.
Les fonctions
parsage($nom_document)
Dans un premier temps, on récupère tous les éléments enfants
directs du noeud racine
zcode. Là, c'est très simple : nous savons que "
zcode" contient
forcément des enfants, ou alors c'est que vous n'avez strictement rien mis entre
<zcode> et
</zcode>. Partant de ce principe, nous allons envoyer "
zcode" vers la fonction
parsage_enfant, qui se charge de parcourir l'arbre XML.
Code : PHP 1
2
3
4
5
6
7
8
9
10
11
12
13
14 | <?php
function parsage($document)
{
$document_xml = new DomDocument;
$document_xml->load($document);
$elements = $document_xml->getElementsByTagName('zcode');
$resultat_html = '';
$arbre = $elements->item(0);
$resultat_html = parsage_enfant($arbre);
return $resultat_html;
}
?>
|
parsage_normal($noeud, $contenu_a_inserer)
Cette fonction est primordiale. En fait, c'est la seule "vraie" fonction de parsage

, car c'est la seule qui va vraiment remplacer les balises zcode en html. Cette fonction renvoie invariablement un noeud parsé.
Nous allons dresser trois tableaux : l'un pour les balises ouvrantes (
<strong>,
<span couleur="$1">, etc), l'autre pour les balises qui ferment (
</strong>,
</span>,
</div>, etc), le dernier pour les attributs qui correspondent à une balise ("
couleur" =>
nom, "
taille" =>
valeur, etc). On utilisera
str_replace pour placer les attributs dans la première balise (
$1 pour le premier attribut,
$2 pour le second). On devra également prendre en compte la balise "
image" qui fonctionne un peu différemment.
Je dois également mentionner le cas où nous devrons parfois parser une balise dont le contenu a déjà été parsé par d'autres fonctions. Dans ce cas, nous voulons récupérer le code html déjà créé et parsé. Nous devons donc ajouter une seconde variable de fonction (
$contenu_a_inserer) pour parer à cette éventualité.
Enfin, nous allons nous occuper des attributs. Pour récupérer un attribut d'un noeud (de classe
DomNode), on récupère la variable
attributes qui est de type DomNameNodeMap. Pour obtenir un attribut sous forme de noeud (
DomNode), on utilise la méthode
getNamedItem($nom_de_lattribut). Je vous préconise également de vous servir de la méthode
hasAttributes sur un noeud pour savoir s'il contient des attributs. Elle renvoie
true s'il y en a et
false si ce n'est pas le cas.
Ultime précision, les dernières lignes relèvent du saut de ligne. Je l'ai empêché à l'intérieur des
<li> et autres
<ul> pour éviter quelques bugs.
Code : PHP 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102 | <?php
function parsage_normal($noeud, $contenu_a_inserer='')
{
$balise_1 = array('gras' => '<strong>',
'italique' => '<span class="italique">',
'position' => '<div class="$1">',
'flottant' => '<div class="flot_$1">',
'taille' => '<span class="$1">',
'couleur' => '<span class="$1">',
'police' => '<span class="$1">',
'attention' => '<span class="rmq $1">',
'liste' => '<ul>',
'puce' => '<li>',
'lien' => '<a href="$1">',
'image' => '<img src="$1" alt="$2" />',
'citation' => '<span class="citation">',
'#text' => ''); // Tableau des balises ouvrantes
$balise_2 = array('gras' => '</strong>',
'italique' => '</span>',
'position' => '</div>',
'flottant' => '</div>',
'taille' => '</span>',
'couleur' => '</span>',
'police' => '</span>',
'attention' => '</span>',
'information' => '</span>',
'liste' => '</ul>',
'puce' => '</li>',
'lien' => '</a>',
'image' => '',
'citation' => '</span>',
'#text' => ''); // Tableau des balises fermantes
$attributs = array('position' => 'valeur',
'flottant' => 'valeur',
'taille' => 'valeur',
'couleur' => 'nom',
'police' => 'nom',
'lien' => 'url',
'image' => 'legende',
'citation' => 'auteur'); // Tableau des attributs
$nom = $noeud->nodeName; // On récupère le nom du noeud
if(!empty($contenu_a_inserer)) // On détermine si on veut spécifier du contenu pré-parsé.
{
$contenu = $contenu_a_inserer; // Si c'est le cas, on met la variable de fonction en contenu
}
else
{
$contenu = $noeud->nodeValue; // Sinon, le contenu du noeud.
}
$premiere_balise = $balise_1[$nom]; // Première balise (ouvrante)
if($noeud->hasAttributes() and $nom != 'image') // On remplace les attributs (sauf pour les images)
{
$un = $noeud->attributes->getNamedItem($attributs[$nom])->nodeValue; // Récupération de la valeur de l'attribut
$premiere_balise = str_replace("$1", $un, $premiere_balise); // On remplace la valeur $1 par celle de l'attribut
}
if($nom == 'image') // Cas particulier des images
{
$un = $contenu; // Dans ce cas, c'est $1 qui récupère le contenu du noeud (l'url de l'image).
$premiere_balise = str_replace("$1", $un, $premiere_balise);
if($noeud->hasAttributes()) // Si l'image contient une légende (attribut $2)
{
$deux = $noeud->attributes->getNamedItem('legende')->nodeValue; // Récupération de l'attribut "legende".
}
else // Par défaut, la légende (alt) est Image.
{
$deux = 'Image';
}
$premiere_balise = str_replace("$2", $deux, $premiere_balise);
$intermediaire = $premiere_balise;
}
else // Cas général
{
$intermediaire = $premiere_balise . $contenu . $balise_2[$nom]; // On assemble le tout
if($nom == 'liste' or $nom == 'puce')
{
$intermediaire = preg_replace("#<ul>(\s)*<li>#sU", "<ul><li>", $intermediaire);
$intermediaire = preg_replace("#</li>(\s)*<li>#sU", "</li><li>", $intermediaire);
$intermediaire = preg_replace("#</li>(\s)*</ul>#sU", "</li></ul>", $intermediaire);
}
if($nom == 'zcode')
{
$intermediaire = nl2br($intermediaire); // On saute des lignes au résultat final
}
}
return $intermediaire; // On renvoie le texte parsé.
}
?>
|
Tantan ! Une bonne chose de faite !
parsage_enfant($noeud)
On va, dans cette fonction, faire la part des choses entre des noeuds avec ou sans enfants. Ceux qui en ont vont vers
parsage_normal tandis que ceux qui contiennent des éléments enfants sont redirigés vers...
parsage_enfant lui-même ! Vicieux, non ?

De cette manière, on parcourt l'arbre XML jusqu'à ce qu'on atteigne les branches les plus éloignées, en quelque sorte.
Code : PHP 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | <?php
function parsage_enfant($noeud)// Fonction de parsage d'enfants
{
if(!isset($accumulation)) // Si c'est la première balise, on initialise $accumulation
{
$accumulation = '';
}
$enfants_niv1 = $noeud->childNodes; // Les enfants du noeud traité
foreach($enfants_niv1 as $enfant) // Pour chaque enfant, on vérifie...
{
if($enfant->hasChildNodes() == true) // ... s'il a lui-même des enfants
{
$accumulation .= parsage_enfant($enfant); // Dans ce cas, on revient sur parsage_enfant
}
else // ... s'il n'en a plus !
{
$accumulation .= parsage_normal($enfant); // On parse comme un noeud normal
}
}
return parsage_normal($noeud, $accumulation);
}
?>
|
Le jour de gloire est arrivé
Pour parser, il vous suffit d'écrire ceci :
Code : PHP1
2
3 | <?php
echo parsage('zcode.xml'); // Mettez le nom du fichier xml
?>
|
Et

!
DOM sur php.net
N'oubliez pas de consulter régulièrement
php.net à la page DOM : très utile. Vous y trouverez toutes les classes. Leurs méthodes sont listées en premier, puis viennent leurs variables dans un tableau. Indispensable !
Allez plus loin grâce à DOM
Si vous êtes un peu curieux, vous avez remarqué que mon parseur ne gère pas :
- les caractères accentués (é, à, ç, ...)
- les smilies
- quelques balises zcode
- la balise "code"
- les erreurs de rédaction (balises mal fermées, ...).
Ce qui, au final, fait pas mal de choses

! Je n'ai pas abordé ces notions pour ne pas vous balancer trop d'informations.
Mais vous pouvez faire certaines choses par vous-mêmes ! Voici quelques pistes pour vous exercer et améliorer votre parseur.
Coloration du code
Vous allez pouvoir colorer votre code grâce à GeSHi (
consultez ce tuto pour découvrir GeSHi). Je vous recommande de vous entraîner avec ce parsage, ça va vous former.
Il faut d'abord que vous transformiez le contenu des noeuds "
code" en section
CDATA. Je vous laisse chercher comment faire. Puis, il va falloir qu'à chaque noeud "
code" rencontré, on récupère le contenu de la section
CDATA. À tout hasard, j'ajoute qu'une instance de
DOMNode a une variable qui renferme le premier noeud,
firstChild. Je précise aussi que pour récupérer le contenu d'une section
CDATA, on utilise la variable d'instance "
data".
Je ne vous en dis pas plus, bon courage !
...
C'est bon ? Allez, je vous donne quelques pistes :
Secret (cliquez pour afficher)
Notez que c'est encore à améliorer, car le code ci-dessus ne parse pas tous les langages... cherchez du côté des attributs "
type" et de leur contenu

.
Code : PHP 1
2
3
4
5
6
7
8
9
10
11 | <?php
include_once('geshi.php');
function parsage_cdata($cdata, $type_code = 'php')
{
$geshi =& new GeSHi($cdata, $type_code);
$geshi->set_header_type(GESHI_HEADER_NONE);
$parse = $geshi->parse_code();
$resultat = '<span class="code">Code : '. $type_code .'</span><div class="code2 '. $type_code .'">'. $parse .'</div>';
return $resultat;
}
?>
|
Voici la fonction
parsage_cdata :
Code : PHP 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 | <?php
function parsage_enfant($noeud)// Fonction de parsage d'enfants
{
if(!isset($accumulation)) // Si c'est la première balise, on initialise $accumulation
{
$accumulation = '';
}
$enfants_niv1 = $noeud->childNodes; // Les enfants du noeud traité
foreach($enfants_niv1 as $enfant) // Pour chaque enfant, on vérifie...
{
if($enfant->nodeName == 'code')
{
$cdata = $enfant->firstChild->data;
$type_code = $enfant->attributes->getNamedItem('type')->nodeValue;
$resultat_html .= parsage_cdata($cdata, $type_code);
}
elseif($enfant->hasChildNodes() == true) // ... s'il a lui-même des enfants
{
$accumulation .= parsage_enfant($enfant); // Dans ce cas, on revient sur parsage_enfant
}
else // ... s'il n'en a plus !
{
$accumulation .= parsage_normal($enfant); // On parse comme un noeud normal
}
}
return parsage_normal($noeud, $accumulation);
}
?>
|
La nouvelle fonction
parsage_enfant :
Remplacez les crochets par des chevrons ! J'ai dû effectuer ce changement pour éviter un bug du zcode... N'oubliez pas de faire cette modif, ou ça ne marchera pas !
Code : PHP1
2
3 | <?php
$chaine = preg_replace("#[code(\stype="(.+)")](.+)[/code]#isU", "[code type="$2"]<![CDATA[$3]]>[/code]", $chaine);
?>
|
Pour transformer en
CDATA, insérez la REGEX ci-dessous dans
parsage et modifiez un peu le code au besoin.
Vous voyez, ce n'était pas si dur que ça.
Gestion des caractères accentués
Nous autres, pauvres Français, utilisons des accents dont nos confrères anglophones se moquent royalement

. Laissez-moi vous expliquer : les développeurs de DOM sont sûrement anglais, ou n'ont pas voulu s'embêter à gérer les caractères accentués. On ne va pas leur en vouloir, mais c'est quand même un grave problème pour nous. Exemple :
Code : Autre
Avouons que ce n'est pas lisible. Bien que l'en-tête du document XML contienne l'encodage, DOM l'ignore (quel grincheux

) et code tout en
utf-8, incompatible avec notre propre norme qui est, je le rappelle,
ISO-8859-1. Conclusion, nous allons
à la sortie du parsage décoder tout ce joyeux bazar pour obtenir du bon français, digne des plus grands auteurs classiques. La solution tient en un mot (ou une fonction) :
utf8_decode. Notre sauveur

. C'est donc la dernière ligne de la fonction
parsage($document) qu'il faut changer :
Code : PHP1
2
3 | <?php
return utf8_decode($resultat_html);
?>
|
Et voilà !
Je tiens à dire que le problème d'encodage n'est pas aussi simple que ce je viens d'expliquer sommairement. Il est tout à fait possible d'obtenir des caractères accentués en UTF-8, comme le fait le Site du Zér0. C'est ? très grossièrement ? une histoire de "rangement" et d'attributions de valeurs. Par exemple, la valeur "468" renvoie "é" en ISO-8859-1 mais "@" en UTF-8 (ce sont des exemples non vérifiés). Ce dernier encodage est d'ailleurs censé être universel, c'est-à-dire que vous pouvez aussi bien écrire en grec qu'en chinois avec de l'UTF-8. Cependant, des manipulations sont nécessaires pour parvenir à afficher correctement les accents en UTF-8, ce qui n'est d'ailleurs pas dans l'intérêt de tous les sites.
Les "zForms" ou formulaires
Vous savez que la zForm du Site du Zér0 propose deux aperçus : un qui se réalise en temps réel, le second qui génère l'affichage final. Le premier est codé en javascript, le second utilise AJAX ? et donc le parseur PHP. Vous pouvez éventuellement réaliser sans trop de difficultés le second aperçu, le premier devra être retouché et adpaté pour le javascript.
Améliorations diverses
Les smilies. Faut pas les laisser tomber ceux-là, hein ? Avec
str_replace ou des REGEX, par exemple. Essayez aussi de vous entraîner à utiliser DOM en tentant de parser les balises "
tableau", elles sont très intéressantes. Pensez aussi à la gestion de chaînes de caractères contenant le texte à parser, ou encore un module de sauvegarde.
Et pour gérer les erreurs de syntaxe ?
Alors, il faudrait parler des
DTD, ce serait trop long. Cependant, cela fera peut-être l'objet d'un futur tuto. Notez aussi qu'on peut parser en utilisant des feuilles de style XSLT, mais c'est encore une autre histoire.
N'hésitez pas non plus à créer des fonctions qui gèrent les "exceptions" ? je parle ici de balises originales ?, les erreurs, etc.
La sécurité avec DOM
J'ai vu dans de nombreux forums, et aussi dans les commentaires de ce tuto, de nombreuses interrogations sur la
sécurité d'un tel système. Vous n'êtes pas sans savoir que la sécurité est quelque chose de très important en informatique. Se poser la question à ce sujet est donc tout à fait justifié. Nous allons considérer le cas de tentatives de violation de sécurité simples.
EXEMPLE 1 : une des failles les plus connues consiste à insérer dans un formulaire du contenu javascript exécutable. Par exemple, dans un système non sécurisé, l'insertion de :
Code : JavaScript1
2
3 | <script type='text/javascript'>
alert('Mouahaha ton système n\'est pas sécurisé.');
</script>
|
affichera un message : "
Mouahaha ton système n'est pas sécurisé.". Évidemment, par la suite, on pourrait exploiter la faille de manière bien plus nuisible.
Dans le cas d'un parseur XML, tout va pour le mieux. DOM interprète cet extrait de code comme un noeud normal, et l'envoie donc à
parsage_normal. Dans la fonction
parsage_normal, on recherche pour chaque nom de noeud s'il correspond à une balise html quelconque (exemple : "
<paragraphe>" => "
<p>"). Si ce n'est pas le cas, la balise et son contenu ne sont pas parsés (au sens qu'ils ne seront pas traduits en xHTML), car
$premiere_balise ne contient rien.
$balise_2 non plus. Quand on construit
$intermediaire, la variable ne contient que le contenu du noeud script, soit : "
alert('Mouahaha ton système n'est pas sécurisé.'');". Et c'est d'ailleurs tout ce qui s'affichera à l'écran

.
EXEMPLE 2 : on pourrait également introduire le code suivant.
Code : Autre1
| <meta http-equiv='refresh' content='0; url=http://hacker.php' /> |
Là, c'est carrément imparable. Si "
meta" ne fait pas partie du zCode (ou de votre langage), rien ne s'affichera.
On peut toujours rechercher des moyens de sécuriser davantage son système, à vous de trouver les bons endroits où placer quelques fonctions intéressantes.
Comment intégrer intelligemment son parseur à son site ?
J'ai remarqué que beaucoup de personnes, notamment dans les commentaires, hésitaient sur la façon d'utiliser un parseur XML. Je vous propose un modèle parmi d'autres, en supposant que vous utilisez MySQL comme base de données. De plus, je considère que vous souhaitez qu'on puisse :
- écrire du contenu (dans les forums, les tutoriels, le livre d'or, que sais-je encore
),
- éditer n'importe quel contenu en zCode,
- l'afficher correctement en xHTML, bien sûr,
- exporter son contenu en zCode dans des fichiers XML.
Je vous propose ceci :
Comment bien intégrer un parseur XML à son site
Évidemment, à vous de déterminer quelle organisation vous correspond le mieux. Là, seuls le papier et le crayon seront vos compagnons, mais aussi vos plus sûrs alliés !
Erreurs de code
Autant vous le dire franchement, c'est rare que ça marche du premier coup, surtout si vous modifiez le code que je vous ai proposé

. C'est une gymnastique très surprenante au début, voilà pourquoi il est capital que vous maîtrisiez parfaitement les bases de DOM présentées ici. Voici les principales sources d'erreur que j'ai pu relever.
- Vérifiez votre version de PHP et sa configuration.
- Utilisez loadHTML au lieu loadXML si vous chargez un document HTML.
- N'oubliez jamais (c'est la cause d'une erreur sur deux) de préciser l'encodage, dans le fichier XML ou bien dans votre code même.
- Vérifiez que votre document XML est correctement formé.
Le problème du code ci-dessus, c'est que les messages d'erreur indiquent rarement les bonnes lignes. N'hésitez pas non plus à tester les autres lignes en cas de bugs.
Le choix de DOM
Ce tutoriel touche à sa fin. Vous avez maintenant une idée claire de ce que c'est que le parsage XML, au moins dans ses grandes lignes. C'est donc maintenant que vous savez dans quoi vous vous lancez en décidant d'utiliser DOM, et pas des REGEX.
N'oubliez pas qu'on ne choisit pas un outil pour montrer ou dire qu'on l'utilise, mais seulement si cet outil s'avère utile pour son projet.
DOM a d'énormes avantages d'un point de vue structurel et organisationnel. En revanche, pour certains langages, les REGEX seront bien plus rapides et adaptées. Le choix de l'une ou l'autre de ces technologies va avoir une influence très forte sur votre site entier. Dressez d'abord une liste de ce dont vous avez besoin, et comparez avec les fonctionnalités proposées par DOM, les REGEX. Prenez celui qui ira le mieux, en faisant la balance entre court terme et long terme. DOM demande beaucoup d'investissement (intellectuel, bien sûr, pas financier

), ne vous lancez pas dedans à la légère.
Après ce petit conseil d'ami, passons au QCM

!
Vous connaissez maintenant les bases de DOM. J'espère que cette initiation vous aura plu et vous aura donné envie d'utiliser cette API dans votre site.
Alors au travail !
A vos claviers, et bon code !
J'aimerais remercier toutes les personnes qui ont commenté mon tutoriel. Elles contribuent beaucoup à l'amélioration du contenu, n'hésitez donc pas vous non plus à y participer ! Je ne réponds pas instantanément, mais je prends note des remarques avant de faire une mise à jour. Vous y trouverez des extraits de codes dont je ne peux pas parler dans le tuto, et bien d'autres choses encore.