Validation d’un document XML à l’aide d’un schéma XSD en PHP

Après plusieurs articles des nouveautés technologiques, des infographies et divers autres sujets, je vous propose un nouveau billet technique présentant comment valider vos fichiers XML en langage PHP. Quel est le but ? Assurer l’intégrité des données en validant des documents XML par rapport à un schéma XML en PHP.

Vous, développeurs PHP, vous avez sûrement déjà eu besoin des services d’un langage de balisage extensible, qu’est XML (Extensible Markup Language), et d’un parseur pour analyser les données. Mais vous avez sûrement déjà dû aussi besoin d’un validateur pour contrôler le contenu d’un document XML ? Si ce n’est pas le cas, je pense que vous n’avez pas compris l’intérêt d’un langage extensible de balisage…
Heureusement, vous pouvez facilement accomplir cela en PHP. Cet article va vous montrez comment valider des documents XML au sein de PHP et de déterminer la cause d’échecs de validation. Allez go !

Pourquoi la validation XML ?

XML est un langage de balisage qui vous permet, en tant que développeur, de définir vos propres balises des éléments et ainsi créer votre propre langage personnalisé. Ce dernier est alors utilisé pour transporter, mais pas nécessairement en affichage, des données de façon indépendante de la plateforme. Le langage est défini par l’utilisation de balises, un peu comme Hypertext Markup Language (HTML).

Validation d'un document XML à l'aide d'un schéma XSD en PHP

XML a gagné en popularité ces dernières années parce qu’il représente le meilleur des deux mondes : Il est facilement lisible par les humains et aussi bien par les ordinateurs. Le langage XML est exprimé avec une structure arborescente contenant des éléments et des attributs décrivant les données clés.
Les noms d’éléments et d’attributs sont généralement écrits dans un langage simple, toujours dans un soucis que les humains puissent les lire. Ils sont également très structurés (afin que les ordinateurs puissent les analyser). Cette syntaxe est reconnaissable par son usage des chevrons (< >).

Maintenant, par exemple, vous créez votre propre document XML, appelé informations permettant de définir vos informations personnelles.

Schéma XML (XSD)

Tout d’abord, vous créez un schéma XML (XSD) permettant de définir la structure et le type de contenu de votre document XML.

Voici un exemple :

[sourcecode language= »xml »]
<?xml version="1.0"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xsd:element name="informations">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="personne">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="nom" type="xsd:string"/>
<xsd:element name="prenom" type="xsd:string"/>
<xsd:element name="age" type="xsd:integer"/>
<xsd:element name="mail" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
[/sourcecode]

Il s’agit, tout à fait intentionnellement, d’un exemple assez simple. L’élément racine est appelé informations. Il est l’élément parent d’un ou plusieurs éléments personne, dont chacun est le parent de quatre autres éléments. Le premier élément est le nom de la personne (nom), le deuxième le prénom (prenom), le troisième étant l’âge de la personne (age). Et, enfin, le dernier élément est le mail de la personne (mail).
Les deux premiers et le dernier de ces éléments fils sont définis comme des chaînes (string), tandis que l’élément age est défini comme un entier (integer).

Document XML

Maintenant, dites vous que vous voulez créer un document XML en fonction du schéma décrit ci-dessus. Il pourrait ressembler à celui-ci :

[sourcecode language= »xml »]
<?xml version="1.0"?>
<informations>
<personne>
<nom>Poiron</nom>
<prenom>Yohann</prenom>
<age>Vingt-cinq</age>
<mail>yohann79@gmail.com</mail>
</personne>
</informations>
[/sourcecode]

Il s’agit d’une instance simple du schéma XSD présenté dans la deuxième partie. Dans ce cas, l’instance de document liste une seule personne. Le nom de cette dernière est Poiron, le prénom Yohann. L’âge de la personne est de Vingt-cinq ans et enfin son adresse mail est yohann79@gmail.com. Vous l’aurez compris cette personne bah c’est moi :)

Voici la question : Comment savez-vous que le document XML écrit est une instance propre du schéma XSD défini dans la deuxième partie ? En fait, il ne l’est pas ! (ce qui est également intentionnel).

En effet, l’élément age tel qu’il est défini dans le schéma est de type xsd:integer. Pourtant, dans le document XML l’élément age contient en fait un mot (Vingt-cinq) et non pas un nombre entier.

Le but de la validation XML est d’attraper exactement ce genre d’erreurs. Une validation veille à ce qu’un document XML corresponde aux règles définies dans son schéma.

En continuant avec cet exemple, lorsque vous tentez de valider le document XML, vous obtenez une erreur. Vous corriger cette erreur (en changeant le Vingt-cinq à un 25) avant d’utiliser le document dans votre application.

La validation XML est très importante dans une application, car vous voulez détecter les erreurs le plus tôt possible dans le processus de l’échange d’information. Dans le cas contraire, des résultats imprévisibles peuvent se produire lorsque vous essayez d’analyser un document XML valide et qu’il contient des types de données ou une structure inattendue.

Validation d'un document XML à l'aide d'un schéma XSD en PHP - Validation réussie

Simple analyse XML en PHP

Il va bien au-delà du champ d’application de cet article de donner un aperçu exhaustif de l’analyse des documents XML en PHP. Cependant, nous allons voir les bases de chargement d’un document XML en PHP.

Juste pour conserver des choses simples, nous allons continuer à utiliser le schéma défini dans la deuxième partie et le document XML de la troisième partie. Le code source ci-dessous montre un peu de code PHP de base pour charger le document XML.

[sourcecode language= »php »]
<?php
$file = "informations.xml";
$schema = "informations.xsd";

// Instanciation d’un DOMDocument
$dom = new DOMDocument("1.0");

// Charge du XML depuis un fichier
$dom->load($file);
?>
[/sourcecode]

Rien de bien compliqué. Vous utilisez la classe DOMDocument pour charger le document XML, appelé ici informations.xml.

Notez que pour que ce code fonctionne sur votre propre serveur Web, le fichier informations.xml doit se trouver au même endroit que le code PHP lui-même.

À ce stade, il est tentant de commencer l’analyse du document XML. Cependant, comme vous l’avez vu, il est préférable d’abord de valider le document pour s’assurer qu’il répond aux spécifications du langage prévues dans le schéma.

Validation XML simple en PHP

Continuez d’ajouter du code PHP dans l’exemple précédent en insérant un code de validation simple, comme ci-dessous :

[sourcecode language= »php »]
<?php
$file = "informations.xml";
$schema = "informations.xsd";

// Instanciation d’un DOMDocument
$dom = new DOMDocument("1.0");

// Charge du XML depuis un fichier
$dom->load($file);

// Validation du document XML
$validate = $dom->schemaValidate($schema) ?
"<b>DOMDocument::schemaValidate() Valid schema!</b>" :
"<b>DOMDocument::schemaValidate() Generated Errors!</b>";

// Affichage du résultat
echo $validate;
?>
[/sourcecode]

Une fois de plus, notez que le fichier du schéma (informations.xsd) doit être dans le même répertoire où le code PHP est situé. Dans le cas contraire, PHP renverra une erreur.

Ce nouveau code appelle la méthode schemaValidate() de l’objet DOMDocument qui a chargé le document XML.
La méthode accepte un paramètre : l’emplacement du schéma XML utilisé pour valider le document XML.
La méthode renvoie une valeur booléenne où, true indique une validation réussie et false indique une validation échouée.

Maintenant, vous allez déployer le code PHP sur votre propre serveur Web. Appeler le, par exemple, test_validation_xml.php et copier le fichier à la racine de votre serveur Web.
Veillez à ce que le document XML et le schéma XML soient dans le même répertoire. Une fois de plus, PHP signale une erreur si ce n’est pas le cas.

Ouvrez votre navigateur, et allez à l’adresse suivante : http://localhost/test_validation_xml.php. Vous devriez avoir ceci à l’écran :

Validation d'un document XML à l'aide d'un schéma XSD en PHP - Test de la validation sur un serveur Web

La bonne nouvelle est que la validation du schéma fonctionne. Elle devait retourner une erreur, et elle l’a fait.

La mauvaise nouvelle c’est que vous n’avez aucune idée où se situe l’erreur dans le document XML. D’accord, vous le savez parce que j’ai mentionné la source de l’erreur plus tôt dans l’article, mais on va prétendre que vous ne savez pas d’où vient l’erreur, d’accord ?

Il y a une erreur, mais où ?

Pour me répéter : La mauvaise nouvelle c’est que vous n’avez aucune idée où se situe l’erreur dans le document XML.

Ce serait bien si le code PHP pouvait vous signaler l’emplacement de l’erreur, ainsi que la nature de celle-ci, de sorte à prendre des mesures correctives. Quelque chose du genre : « Hey ! Je ne peux pas accepter une chaîne pour l’âge  » serait bien !

Pour afficher l’erreur qui s’est produite, vous pouvez utiliser la fonction libxml_get_errors(). Malheureusement, le texte de sortie de cette fonction ne spécifie pas où dans le document XML l’erreur s’est produite. Au lieu de cela, il identifie où dans le code PHP une erreur a été rencontrée. Bon ce n’est pas ce que l’on cherche, continuons.

Il est une autre fonction PHP appelée libxml_use_internal_errors(). Celle-ci accepte une valeur booléenne comme seul paramètre. Si vous le réglez à vrai (true), alors cela signifie que vous désactivez le rapport d’erreurs libxml, pour permettre votre propre récupération des erreurs.
Bien sûr, cela signifie que vous devez écrire un peu plus de code, mais le compromis est que le rapport d’erreurs sera spécifique. À long terme, vous économiserez beaucoup de temps.

Voici le code modifié :

[sourcecode language= »php »]
<?php

function libxml_display_error($error) {
$return = "<br/>\n";
switch ($error->level) {
case LIBXML_ERR_WARNING:
$return .= "<b>Warning $error->code</b>: ";
break;
case LIBXML_ERR_ERROR:
$return .= "<b>Error $error->code</b>: ";
break;
case LIBXML_ERR_FATAL:
$return .= "<b>Fatal Error $error->code</b>: ";
break;
}
$return .= trim($error->message);
if ($error->file) {
$return .= " in <b>$error->file</b>";
}
$return .= " on line <b>$error->line</b>\n";

return $return;
}

function libxml_display_errors($display_errors = true) {
$errors = libxml_get_errors();
$chain_errors = "";

foreach ($errors as $error) {
$chain_errors .= preg_replace(‘/( in\ \/(.*))/’,  », strip_tags(libxml_display_error($error)))."\n";
if ($display_errors) {
trigger_error(libxml_display_error($error), E_USER_WARNING);
}
}
libxml_clear_errors();

return $chain_errors;
}

// Activer "user error handling"
libxml_use_internal_errors(true);

$file = "informations.xml";
$schema = "informations.xsd";

// Instanciation d’un DOMDocument
$dom = new DOMDocument("1.0");

// Charge du XML depuis un fichier
$dom->load($file);

// Validation du document XML
$validate = $dom->schemaValidate($schema);

// Affichage du résultat
if ($validate) {
echo "<b>DOMDocument::schemaValidate() Valid schema !</b>";
} else {
echo "<b>DOMDocument::schemaValidate() Generated Errors !</b><br /><br />";
libxml_display_errors();
}
?>
[/sourcecode]

Notez tout d’abord les fonctions rajoutées en haut du code source. La première libxml_display_error() accepte un objet LibXMLError comme seul paramètre. Ensuite un switch permet de déterminer le type de l’erreur, en parcourant la sévérité de l’erreur (une des constantes suivantes : LIBXML_ERR_WARNING, LIBXML_ERR_ERROR ou LIBXML_ERR_FATAL). Lorsque le niveau est déterminé, le code source produit une chaîne qui indique le niveau approprié, en fonction du code de l’erreur.

Alors, par la suite, deux choses sont faites. Tout d’abord, l’objet d’erreur est examiné pour déterminer si celui-ci contient un nom de fichier. Si oui, alors la valeur de ce dernier est ajoutée au message d’erreur permettant ainsi de déclarer l’emplacement du fichier. Ensuite, la ligne de propriété est ajoutée au message d’erreur permettant à l’utilisateur de savoir exactement où dans le fichier XML l’erreur s’est produite. Inutile de dire que c’est extrêmement important lors du débogage.

Il convient également de noter que libxml_display_error() produit simplement une chaîne décrivant l’erreur. L’affichage réelle de l’erreur à l’écran est laissée par l’appel de la fonction libxml_display_errors(). Elle ne prend aucun paramètre. La première chose que cette fonction fait est l’appel à la méthode libxml_get_errors(). Cette dernière retourne un tableau d’objets LibXMLError qui représente toutes les erreurs rencontrées lors de l’appel à la méthode schemaValidate() sur le document XML.

Ensuite, pour chaque objet d’erreur, la fonction libxml_display_error() est appelée réalisant le traitement décrit précédemment. Quel que soit le résultat, la chaîne est retournée par cette fonction et affichée à l’écran. Un grand avantage de la manipulation de ces erreurs de cette façon, est que toutes sont imprimées en une seule fois. Cela signifie que vous avez seulement besoin d’exécuter le code une fois pour afficher toutes les erreurs spécifiques à un document XML.

Enfin, libxml_clear_errors() efface les erreurs rencontrées récemment par la méthode schemaValidate(). Cela signifie que si schemaValidate() est exécutée à nouveau dans la même séquence de code, vous commencerez avec une table vide, et que de nouvelles erreurs seront signalées. Si vous ne le faites pas, et que vous exécutez à nouveau la méthode schemaValidate(), toutes les erreurs de la première invocation à cette fonction restent dans le tableau retourné par libxml_get_errors(). Évidemment, cela pose des problèmes si vous êtes à la recherche d’une nouvelle série d’erreurs.

Deuxième essai

Maintenant, il est temps de tester à nouveau. Déplacez le nouveau fichier PHP sur votre serveur Web. Comme avant, s’assurer que le schéma (XSD) et le fichier XML sont dans le même répertoire que le fichier PHP. Pointez votre navigateur sur http://localhost/test_validation_2.php et maintenant vous devriez voir quelque chose comme ceci :

Validation d'un document XML à l'aide d'un schéma XSD en PHP - Test de la validation sur un serveur Web - Affichage des erreurs

Eh bien, c’est assez descriptif, n’est-ce pas ? Le message d’erreur vous indique sur quelle ligne l’erreur s’est produite. Il vous indique également où le fichier se trouve (comme si vous ne saviez pas !). Et il vous dit exactement pourquoi l’erreur s’est produite. C’est l’information que vous pouvez utiliser.

Régler le problème

Vous pouvez maintenant fermer le fichier PHP et travailler sur la résolution du problème de votre document XML.

Parce que l’erreur se serait produite sur la ligne 6 du document XML, c’est une bonne idée de regarder sur cette ligne et de voir ce qu’il contient. Sans surprise, la ligne 6 est l’emplacement de l’élément age. Et, comme vous le regardez attentivement, vous avez soudainement une révélation que Vingt-cinq est une chaîne, pas un numéro. Donc, vous modifiez la chaîne Vingt-cinq par le chiffre 25.

Voici a quoi la version finale du document XML devrait ressembler :

[sourcecode language= »xml »]
<?xml version="1.0"?>
<informations>
<personne>
<nom>Poiron</nom>
<prenom>Yohann</prenom>
<age>25</age>
<mail>yohann79@gmail.com</mail>
</personne>
</informations>
[/sourcecode]

Maintenant, copiez ce nouveau fichier XML sur votre serveur Web. Et, une fois de plus, pointez votre navigateur sur http://localhost/test_validation_2.php. Vous devriez avoir ceci à l’écran :

Validation d'un document XML à l'aide d'un schéma XSD en PHP - Test de la validation sur un serveur Web - Document XML valide

Ce sont d’excellentes nouvelles pour deux raisons. Premièrement, cela signifie que le code de validation fonctionne correctement parce que le document XML est, en fait, valide. Deuxièmement, vous venez probablement de valider votre premier document XML en PHP. Félicitations !

Comme je le conseille toujours, il est maintenant temps de bricoler. Modifiez le fichier informations.xsd pour en faire un schéma plus complexe. Modifiez informations.xml pour en faire un exemple plus complet de ce schéma – par exemple rajoutez des éléments personne. Copiez ces fichiers sur le serveur Web et, encore une fois, exécutez http://localhost/test_validation_2.php. Voir ce qui se passe, et corriger votre fichier si ce dernier contient des erreurs.

En outre, notez que lorsque vous bricolez votre document XML et votre schéma, vous n’avez pas besoin de modifier le code PHP.

Démos et téléchargement

Vous pouvez télécharger la démo de l'article dans un seul fichier ZIP.

Conclusion

En PHP, il est facile pour les développeurs de valider des documents XML. L’utilisation de la classe DOMDocument en collaboration avec la méthode schemaValidate(), vous permet de vous assurer que vos documents XML sont conformes aux spécifications dans leurs schémas respectifs.

Ceci est important pour assurer l’intégrité des données dans vos applications logicielles.

Ressources

Pour en savoir plus :

  • DOMDocument : Consultez le manuel PHP pour cette classe, qui représente un document HTML ou XML entier. C’est la racine de l’arbre document.
  • Validation XML : L’explication par Wikipedia du processus de validation d’un document écrit en XML.
  • XML pour les développeurs PHP :
    • Partie 1: Les 15 premières minutes de PHP à XML : Mise en œuvre de PHP5 pour le XML
    • Partie 2: Techniques avancées d’analyse syntaxique en XML
    • Partie 3: Techniques avancées de lire, manipuler et écrire des données XML
  • Tutoriel – Validation XML : Comprendre la différence de validation entre une définition de type de document (DTD) et un schéma XML (XSD).