Bonjour à toi, ami zéro ! Si tu viens par ici, c'est que le titre de ce tutoriel t'a accroché, et je vais essayer de ne pas te décevoir. Tu as sûrement l'habitude des cours simples et clairs de M@teo21, et je vais essayer de faire aussi bien que lui (bien que je n'aie pas autant d'expérience dans ce domaine).
Comme son nom l'indique, ce magnifique tutoriel va s'appuyer sur le zCode, donc avant de lire celui-ci, tu dois connaître le zCode. Si tu ne le connais pas, va d'abord lire le premier chapitre rédigé par l'auteur du zCode.
C'est un mini-tuto assez long, mais le sujet est vaste, et surtout vous pourrez réaliser votre propre système de zCode complet à la fin : ça en vaut donc la chandelle !
Introduction : qu'est-ce qu'un système de parsage ?
Le mot parsage nous vient du verbe anglais to parse, qui signifie littéralement analyser et a donné naissance au néologisme français « parser », qui est un simple synonyme d'analyser. C'est une francisation employée couramment dans le monde de la programmation d'aujourd'hui. Le parsage est le fait d'analyser quelque chose. Le parseur est le script qui analyse une donnée d'entrée. Un système de parsage est donc un système d'analyse. Un système de parsage XML est un système d'analyse XML.
Analyse, c'est bien gentil, mais ça sert à quoi d’analyser ?
Généralisation
Un système d'analyse de code en soi ne sert à rien. En effet, ça ne sert à rien de savoir qu'il y a une variable, une chaîne de caractères ou encore un bloc d'instructions à un endroit : il faut bien faire quelque chose de ce code ! C'est pourquoi un système de parsage va rarement seul, et sert à appliquer un traitement à l'objet d'analyse. De cette façon, beaucoup de personnes ont étendu le mot parsage à système de transformation. Ce système de parsage utilise un parseur, qui fait la moitié du travail : l'analyse de la donnée d'entrée. Reste la deuxième moitié : la transformation.
XML est un langage de description de données. XML est l'abréviation de eXtensible Markup Language. C'est un langage de balises (markup language) extensible, c'est-à-dire que l'on peut définir ses propres balises et leur signification.
Buts et origine
À l'origine de XML, il y a le W3C, qui a créé un groupe de travail en 1996. La norme XML 1.0 a été publiée le 10 février 1998. XML avait pour but d'être utilisable sans difficulté sur Internet et de soutenir un grand nombre d'applications. Le langage devait être clair, facile et rapide à écrire tout en étant compréhensible par l'homme. La dernière norme XML est la 1.1 (2e version), publiée le 16 août 2006.
Comme tu peux le voir, XML avait de grandes ambitions... qui ont été réalisées avec succès ! XML est aujourd'hui un standard reconnu ! Grâce à son succès, un vrai monde s'est formé autour de XML (on l'appelle la galaxie XML). En voici les composantes les plus connues :
XPath ;
XLink ;
XSL ;
XQuery ;
DOM ;
RDF.
Les notions du langage
La base du langage est le nœud, ce qui permet de représenter un document XML sous forme d'un arbre.
XML définit des nœuds de 3 types :
élément ;
attribut ;
texte simple.
Un nœud élément peut contenir ou non des nœuds attributs. L'ensemble composé d'un nœud élément, et des nœuds attributs associés au nœud élément s'appelle une balise. Seuls les nœuds éléments peuvent contenir des sous-nœuds, mais ils peuvent en contenir une infinité.
Un document XML possède un prologue (obligatoire depuis la norme 1.1) : le DTD (définition du type de document en français). Cette DTD sert à définir les éléments de la page, ainsi que leurs attributs et les éléments qu'ils peuvent contenir. D'autres prologues sont disponibles, comme le XML Schema, qui intègre toutes les nouveautés de la norme 1.1, mais ils sont encore peu utilisés.
En bref
La norme XML a suscité un grand engouement et possède maintenant une grande communauté active. Le monde XML est éprouvé et peut être utilisé dans beaucoup de langages, dont le PHP, qui intègre pas moins de 6 extensions différentes pour faire du XML. Le XML t'intéresse ? Tu peux aller lire ce tutoriel qui le décrit plus longuement.
Il est maintenant temps d'entrer dans le vif du sujet : la réalisation du parseur pour le zCode !
Regardons les outils à notre disposition pour traiter du XML sur php.net. Recherchons « XML », nous tombons sur cette page.
Voici un parseur XML pour PHP ! La popularité de XML est donc au rendez-vous et nous trouvons des librairies officielles toutes prêtes. :) En vrai, il en existe encore d'autres pour PHP : XMLReader, XMLWriter, DOM, DOMXML et XMLRPC. Mais nous utiliserons XML (la première citée), car elle est utilisable sans installation et dans toutes les versions de PHP.
Regardons maintenant ce qu'elle permet de faire. Lisons un peu la documentation, et nous tombons sur cet exemple :
Voici quelque chose de très intéressant : cette exemple transforme les balises <BOLD> en <B>, les balises <EMPHASIS> en <I>, les balises <LITERAL> en <TT>, et ignorent toutes les autres balises ! Nous n'avons pas encore réfléchi quant au comportement à adopter avec les balises inconnues, mais les ignorer semble pas mal : cela règle un bon nombre de problèmes de sécurité.
C'est presque ce qu'on veut (pas tout à fait, quand même...) : on veut transformer <gras> en <span style="gras">, par exemple !
Attardons-nous donc deux secondes sur le code pour voir comment il fonctionne.
Dans l'exemple, nous pouvons voir la ligne suivante :
Cette ligne indique en fait au parseur des fonctions de « callback » à appeler lorsqu'il rencontre un tag d'ouverture ou de fermeture d'une balise (il faut spécifier les deux). Ici, la fonction de callback pour un élément ouvrant est startElement(), et celle pour un élément fermant est endElement().
La fonction de callback pour un tag d'ouverture prend 3 arguments en paramètre. Le premier vous fournit une référence vers le parseur, le deuxième vous fournit le nom de la balise (BOLD, EMPHASIS, LITERAL, ... ), et enfin les attributs de cette balise (comme par exemple son ID). La fonction de callback pour les tags de fermeture prend les deux mêmes premiers arguments que la fonction de callback pour les tags d'ouverture.
Vous pouvez changer ce comportement en utilisant la commande ci-dessous :
Cette ligne indique également une fonction de callback, mais ce coup-ci, c'est pour le texte littéral, c'est-à-dire ce qu'il y a entre les balises. Cette fonction de callback prend deux paramètres : une référence vers le parseur et les données (le texte).
Ces fonctions de callback jouent un rôle déterminant dans le traitement à appliquer aux données de départ.
Dans la fonction startElement(), nous avons le code suivant :
<?php if (isset($map_array[$name]))
Cette condition permet de repérer si la balise rencontrée doit être remplacée par notre parseur ou non. Cette condition sert à traiter uniquement les balises contenues dans le tableau $map_array, et d'ignorer les autres.
La même condition est présente dans la fonction endElement().
Nous avons compris le principe de fonctionnement du script ; modifions maintenant le tableau $map_array et les deux fonctions startElement et endElement afin qu'elles correspondent à notre besoin : afficher <span style="notre_style_personnalise"> au lieu de nos balises <gras> et <italique> !
<?php
// Fichier source
$file = "data.xml";
$map_array = array(
"GRAS" => "font-weight: bold;",
"ITALIQUE" => "font-style: italic;",
);
function startElement($parser, $name, $attrs)
{
global $map_array;
if (isset($map_array[$name])) {
echo '<span style="'.$map_array[$name].'">';
// ou echo '<span class="'.$name.'">';
}
}
function endElement($parser, $name)
{
global $map_array;
if (isset($map_array[$name])) {
echo "</span>";
}
}
function characterData($parser, $data)
{
echo $data;
}
$xml_parser = xml_parser_create();
// Utilisons la gestion de casse, de manière à être sûrs de trouver la balise dans $map_array
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, "characterData");
if (!($fp = fopen($file, "r"))) {
die("Impossible de trouver le fichier XML");
}
while ($data = fread($fp, 4096)) {
if (!xml_parse($xml_parser, $data, feof($fp))) {
die(sprintf("erreur XML : %s à la ligne %d",
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser)));
}
}
xml_parser_free($xml_parser);
?>
Nous n'avons donc pas modifié le reste de l'exemple, juste le tableau $map_array et les fonctions startElement et endElement. Cet exemple traite le cas d'un fichier source, mais nous verrons l'intégration d'un formulaire dans une prochaine partie.
Nous pouvons (une fois de plus, si vous avez l'habitude de travailler avec la documentation) démontrer la puissance de la documentation PHP : nous avons fait un début de parseur XML pour un langage tel que le zCode en moins de 10 minutes !
Dans cette partie, nous allons regarder de plus près l'utilisation du 3e argument de la fonction de callback pour le parsage des tags d'ouverture de balises. Il va nous permettre de gérer les balises qui ont des attributs, telles que <couleur nom="rose">, par exemple (dont l'attribut nom, vaut « rose »).
Nous allons nous appuyer sur cette balise dans la suite, mais les balises telles que <taille valeur="gros">, <police nom="times">, <lien type="wikipedia"> ou encore <citation nom="Savageman"> fonctionnent exactement sur le même principe et vous devriez pouvoir les reproduire sans aucun problème.
Dans le cas de <couleur nom="rose">, le nom de la balise serait « COULEUR », et le tableau $attrs contiendrait la paire (clé, valeur) (NOM, rose).
Il faut donc que lorsqu'on rencontre la balise « COULEUR », nous vérifions si le nom de la couleur est connu et, si c'est le cas, que nous remplacions la balise par le code (x)HTML qui permettrait au navigateur d'interpréter cette couleur. Je vais personnellement utiliser les styles CSS pour gérer tous les éléments du zCode. Ainsi, <span class="couleur-rose">texte</span> me permet d'afficher un texte rose. Le style CSS associé est le suivant :
.couleur-rose { color: pink; }
Le parsage de la balise couleur pourrait donc se passer de cette façon :
<?php
// Liste des couleurs que l'on souhaite gérer
$liste_couleurs = array('rose', 'noir', 'blanc', 'rouge', 'vert', 'bleu');
function startElement($parser, $name, $attrs)
{
global $liste_couleurs;
if ('COULEUR' == $name && array_key_exists('NOM', $attrs) && in_array($attrs['NOM'], $liste_couleurs)) {
echo '<span class="couleur-'.$attrs['NOM'].'">';
}
}
Explications :<?php ('COULEUR' == $name) permet de vérifier que la balise que l'on va traiter est la balise <couleur> (c'est ce qu'on a vu dans la partie précédente).
<?php (array_key_exists('NOM', $attrs)) permet de vérifier que notre balise <couleur> a bien un attribut nom, et donc qu'elle est bien de la forme <couleur nom="___">. On vérifie que la balise s'appelle COULEUR, puis qu'elle a un attribut NOM, dont nous avons besoin pour récupérer la couleur.
<?php (in_array($attrs['NOM'], $liste_couleurs))
Enfin, on vérifie que l'attribut NOM (je vous rappelle que le parseur met des majuscules automatiquement) existe dans la liste des couleurs autorisées. Si c'est le cas, alors on peut effectuer la transformation.
Ce tutoriel touche à sa fin. J'espère avoir été clair, qu'il vous aura plu, et que vous aurez compris le principe de la transformation. Maintenant c'est à vous de travailler pour faire votre propre zCode personnalisé avec les balises que vous aurez choisies, alors au travail !