65. Utilisation de XML

65.1. Document bien formé

Pour mettre en évidence les règles que doit respecter un document XML pour être bien formé nous allons utiliser l’extension DOM présente dans PHP5.

Comme son nom le laisse deviner elle implémente l’API DOM de niveau 2.

Nous allons utiliser l’objet DOMDocument et plus particulièrement ses méthodes load() et saveXML().

65.1.1. DOMDocument

L’objet DOMDocument va nous permettre d’exploiter un document XML. Il dispose de méthodes pour lire, créer et modifier un document XML.

65.1.1.1. Méthode load()

Cette méthode va nous permettre de "charger" notre document XML dans l’objet DOMDocument à partir d’un fichier.

Comme tout parseur lors de l’utilisation de cette méthode il vérifie que le document soit bien formé

65.1.1.2. Méthode saveXML()

Cette méthode renvoie le document XML sous forme d’une chaîne et va nous permettre d’afficher le document.

65.1.2. Affichage d’un document

Dans cet exemple nous avons plusieurs points qui rendent le document exemple1.xml mal formé.


<?xml version="1.0" encoding="utf-8"?>
<exemple1>
	<baliseSeule>cet élément n’a pas de balise de fin.
	<elementVideIncorrect parmetre="1">
	<element parametreIncorrect=test>La déclaration du paramêtre est incorrecte</element>
		<element1>imbrication<element2> incorrecte de ces 2 éléments</element1></element2>
		<element> qui dispose d’une entité caractère prédéfinie non échappée.</element>
		<balise avec des caractères interdits/>
</exemple1>

Créez le fichier exemple1.php que vous placez dans l’arborescence de votre serveur Web.


<?php
	$document = new DOMDocument();
	$document->load('exemple1.xml');
	echo $document->saveXML();
?>

J'appelle ensuite la page exemple1.php dans mon navigateur. J'obtiens ceci :


Warning: DOMDocument::load() [function.DOMDocument-load]: 
	AttValue: " or ' expected in /var/www/html/exemple1.xml, line: 5 in /var/www/html/exemple1.php on line 3
Warning: DOMDocument::load() [function.DOMDocument-load]: 
	attributes construct error in /var/www/html/exemple1.xml, line: 5 in /var/www/html/exemple1.php on line 3
Warning: DOMDocument::load() [function.DOMDocument-load]: 
	Couldn't find end of Start Tag element line 5 in /var/www/html/exemple1.xml, line: 5 in /var/www/html/exemple1.php on line 3
Warning: DOMDocument::load() [function.DOMDocument-load]:
	Opening and ending tag mismatch: elementVideIncorrect line 4 and element in /var/www/html/exemple1.xml, line: 5 in /var/www/html/exemple1.php on line 3
Warning: DOMDocument::load() [function.DOMDocument-load]:
	Opening and ending tag mismatch: element2 line 6 and element1 in /var/www/html/exemple1.xml, line: 6 in /var/www/html/exemple1.php on line 3
Warning: DOMDocument::load() [function.DOMDocument-load]:
	Opening and ending tag mismatch: element1 line 6 and element2 in /var/www/html/exemple1.xml, line: 6 in /var/www/html/exemple1.php on line 3
Warning: DOMDocument::load() [function.DOMDocument-load]:
	xmlParseEntityRef: no name in /var/www/html/exemple1.xml, line: 7 in /var/www/html/exemple1.php on line 3
Warning: DOMDocument::load() [function.DOMDocument-load]: 
	Specification mandate value for attribute avec in /var/www/html/exemple1.xml, line: 8 in /var/www/html/exemple1.php on line 3
Warning: DOMDocument::load() [function.DOMDocument-load]: 
	attributes construct error in /var/www/html/exemple1.xml, line: 8 in /var/www/html/exemple1.php on line 3
Warning: DOMDocument::load() [function.DOMDocument-load]: 
	Couldn't find end of Start Tag balise line 8 in /var/www/html/exemple1.xml, line: 8 in /var/www/html/exemple1.php on line 3
Warning: DOMDocument::load() [function.DOMDocument-load]:
	Opening and ending tag mismatch: baliseSeule line 3 and exemple1 in /var/www/html/exemple1.xml, line: 9 in /var/www/html/exemple1.php on line 3
Warning: DOMDocument::load() [function.DOMDocument-load]:
	Premature end of data in tag exemple1 line 2 in /var/www/html/exemple1.xml, line: 10 in /var/www/html/exemple1.php on line 3

Modifions notre fichier XML pour qu'il soit correct :


<?xml version="1.0" encoding="utf-8"?>
<exemple1>
        <baliseSeule>cet élément na pas de balise de fin.</baliseSeule>
        <elementVideIncorrect parametre="1" />
        <element parametreIncorrect="test">La déclaration du paramêtre est incorrecte</element>
        <element1>imbrication<element2> incorrecte de ces 2 éléments</element2></element1>
        <element> qui dispose dune entité caractère prédéfinie non échappée <![CDATA[&.</element>
        <balise_avec_des_caracteres_interdits/>
</exemple1>

Après un second essai, j'obtiens à l'affichage :


cet élément n’a pas de balise de fin. La déclaration du paramêtre est incorrecte imbrication incorrecte de ces 2 éléments qui dispose d’une entité caractère prédéfinie non échappée &.

Pour rendre le document plus lisible nous allons ajouter la commande header("Content-type: text/xml") au début du fichier exemple.php.


<?php
        header("Content-type: text/xml");
        $document = new DOMDocument();
        $document->load('exemple1.xml');
        echo $document->saveXML();
?>

Nous indiquons ainsi à notre navigateur que le document envoyé est un document XML. Si le navigateur gère le XML l’affichage sera beaucoup plus clair.

Figure 52. Affichage XML via navigateur

65.2. Les définitions de type de documents

Un document bien formé garanti que le parseur XML utilisé sera capable de le lire. La définition de type de documents va permettre de s’assurer que le document transmis a du sens pour le processeur XML.

Nous allons commencer par un exemple simple qui va représenter les informations liées à un livre.

65.2.1. DTD d’un livre

Nous souhaitons que le document XML résultant dispose des informations suivantes :

  • le titre ;

  • l’auteur ;

  • le code ISBN ;

  • une description ;

  • le type de livre ;

  • des commentaires de lecteurs.

En fonction de ces éléments nous allons définir notre DTD :


adminbdd html # cat livre.dtd 
<!-- DTD de présentation d’un livre. -->
<!ELEMENT livre (titre, auteur, type, description, commentaire*)>
<!ATTLIST livre ISBN CDATA #REQUIRED>
<!ELEMENT titre (#PCDATA)>
<!ELEMENT auteur (#PCDATA)>
<!ELEMENT type (#PCDATA)>
<!ELEMENT description (#PCDATA)>
<!ELEMENT commentaire (#PCDATA)>

Placez cette DTD dans le fichier livre.dtd. Nous allons ensuite définir un premier document XML valide.


adminbdd html # cat livre1.xml 
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE livre SYSTEM "livre.dtd">
<livre ISBN="2-99999-123-4" >
	<titre>Le XML rapide.</titre>
	<auteur>Auteur du livre</auteur>
	<type>Documentation technique</type>
	<description>Cet ouvrage explique les fondements du XML.</description>
	<commentaire>Bon rapport qualité prix.</commentaire>
	<commentaire>Bonne explication des bases.</commentaire>
</livre>

Enregistrez ce document dans le fichier livre1.xml. Pour valider notre document nous allons utiliser la méthode validate() de l’objet DOMDocument. Cette méthode utilise la définition présente dans le document XML pour valider ce dernier. Créez ensuite un fichier PHP livre.php, avec le code suivant :


adminbdd html # cat livre.php 
<?php
        $document = DOMDocument::load("livre1.xml");
        echo ($document->validate()) ? "Le document est valide." : "Le document n’est pas valide.";
?>

Appelez le fichier livre.php vous obtiendrez alors un message qui confirme la validité du document. Si ce n’est pas le cas vérifiez que les trois fichiers sont placés dans le même répertoire sur votre serveur Web.

Bien, procédons au test. J'obtiens bien :


Le document est valide.

Le document est donc conforme à la DTD.

65.2.2. Tests de validité

65.2.2.1. Cardinalité des éléments

Nous allons maintenant considérer que l’élément description est facultatif. Supprimons-le du document XML et actualisons la page livre.php.


adminbdd html # cat livre1.xml 
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE livre SYSTEM "livre.dtd">
<livre ISBN="2-99999-123-4" >
        <titre>Le XML rapide.</titre>
        <auteur>Auteur du livre</auteur>
        <type>Documentation technique</type>
        <commentaire>Bon rapport qualité prix.</commentaire>
        <commentaire>Bonne explication des bases.</commentaire>
</livre>

J'obtiens le message d’erreur suivant :


Warning: DOMDocument::validate() [function.DOMDocument-validate]: 
	Element livre content does not follow the DTD, expecting (titre , auteur , type , description , commentaire*), got (titre auteur type commentaire commentaire ) in /var/www/html/livre.php on line 3
Le document n’est pas valide.

Si l’on ne précise rien dans la DTD l’élément doit être obligatoirement présent. Pour rendre la description facultative nous allons modifier la DTD de notre document.

Nous plaçons le symbole "?" derrière l’élément description. Une fois modifiée la DTD se présente comme suit :


adminbdd html # cat livre.dtd 
<!-- DTD de préntation d’un livre. -->
<!ELEMENT livre (titre, auteur, type, description?, commentaire*)>
<!ATTLIST livre ISBN CDATA #REQUIRED>
<!ELEMENT titre (#PCDATA)>
<!ELEMENT auteur (#PCDATA)>
<!ELEMENT type (#PCDATA)>
<!ELEMENT description (#PCDATA)>
<!ELEMENT commentaire (#PCDATA)>

Après rafraîchissement de la page, nous obtenons de nouveau un document valide.

65.2.2.2. Séquence des éléments

À présent modifiez le fichier livre1.xml de façon à placer l’élément auteur avant l’élément titre et obtenir le document suivant :


adminbdd html # cat livre1.xml 
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE livre SYSTEM "livre.dtd">
<livre ISBN="2-99999-123-4" >
        <auteur>Auteur du livre</auteur>
        <titre>Le XML rapide.</titre>
        <type>Documentation technique</type>
        <description>Cet ouvrage explique les fondements du XML.</description>
        <commentaire>Bon rapport qualité prix.</commentaire>
        <commentaire>Bonne explication des bases.</commentaire>
</livre>

Lorsque vous affichez la page livre.php avec ce nouveau document vous obtenez un message d’erreur puisque les éléments ne sont pas dans l’ordre défini dans la DTD.


adminbdd html # php livre.php 
PHP Warning:  DOMDocument::load(): String not started expecting ' or " in /var/www/html/livre1.xml, line: 1 in /var/www/html/livre.php on line 2

Warning: DOMDocument::load(): String not started expecting ' or " in /var/www/html/livre1.xml, line: 1 in /var/www/html/livre.php on line 2
PHP Warning:  DOMDocument::load(): Malformed declaration expecting version in /var/www/html/livre1.xml, line: 1 in /var/www/html/livre.php on line 2

Warning: DOMDocument::load(): Malformed declaration expecting version in /var/www/html/livre1.xml, line: 1 in /var/www/html/livre.php on line 2
PHP Warning:  DOMDocument::load(): Blank needed here in /var/www/html/livre1.xml, line: 1 in /var/www/html/livre.php on line 2

Warning: DOMDocument::load(): Blank needed here in /var/www/html/livre1.xml, line: 1 in /var/www/html/livre.php on line 2
PHP Warning:  DOMDocument::load(): parsing XML declaration: '?>' expected in /var/www/html/livre1.xml, line: 1 in /var/www/html/livre.php on line 2

Warning: DOMDocument::load(): parsing XML declaration: '?>' expected in /var/www/html/livre1.xml, line: 1 in /var/www/html/livre.php on line 2
PHP Fatal error:  Call to a member function validate() on a non-object in /var/www/html/livre.php on line 3

Fatal error: Call to a member function validate() on a non-object in /var/www/html/livre.php on line 3

65.2.2.3. Contenu mixte

Reprenons notre exemple et faisons évoluer le modèle en fonction du type. Dans le cas de romans nous souhaitons structurer la description. Nous ajoutons pour cela deux balises enfants à description et obtenons la DTD suivante :


adminbdd html # cat livre.dtd 
<!ELEMENT livre (titre, auteur, type, description?, commentaire*)>
<!ATTLIST livre ISBN CDATA #REQUIRED>
<!ELEMENT titre (#PCDATA)>
<!ELEMENT auteur (#PCDATA)>
<!ELEMENT type (#PCDATA)>
<!ELEMENT description (#PCDATA|epoque|resume)*>
<!ELEMENT epoque (#PCDATA)>
<!ELEMENT resume (#PCDATA)>
<!ELEMENT commentaire (#PCDATA)>

Il est alors possible d’ajouter à la description classique ces deux éléments. On parle alors de contenu mixte.


adminbdd html # cat livre1.xml 
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE livre SYSTEM "livre.dtd">
<livre ISBN="2-99999-123-4" >
        <titre>Mon roman</titre>
        <auteur>Auteur du livre</auteur>
        <type>roman</type>
        <description>
                description du livre,
                <epoque>de nos jours</epoque>
                <resume>Resumé du roman</resume>
        </description>
</livre>

adminbdd html # php livre.php 
Le document est valide.

65.2.3. Les entités

Nous allons créer une entité qui comprend le titre du livre :


adminbdd html # cat livre1.xml 
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE livre SYSTEM "livre.dtd"
[
        <!ENTITY titreLivre "Le XML rapide">
]>
<livre ISBN="2-99999-123-4" >
        <titre>&titreLivre;</titre>
        <auteur>Auteur du livre</auteur>
        <type>Documentation technique</type>
        <description>&titreLivre; explique les fondements du XML.</description>
        <commentaire>&titreLivre; a un bon rapport qualité prix.</commentaire>
        <commentaire>Bonne explication des bases.</commentaire>
</livre>

Modifiez ensuite le fichier livre.php avec le code suivant :


adminbdd html # cat livre.php
<?php
        header("Content-type: text/xml");

        $document = DOMDocument::load("livre1.xml");

        if ($document->validate()) {
                echo $document->saveXML();
        } else {
                echo "Le document nest pas valide.";
        }
?>

On obtient alors le résultat suivant :

Figure 53. Utilisation des entités

65.3. XSD (XML Schema Definition)

65.3.1. Schéma d’un livre

Un schéma XML va nous permettre tout comme une DTD de contrôler la validité d’un document XML. Commençons par définir le schéma pour notre exemple de livre :


adminbdd html # cat livre.xsd 
<?xml version='1.0' encoding='UTF-8' ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <xsd:element name="livre" type="livreType" />
        <xsd:complexType name="livreType">
                <xsd:sequence>
                        <xsd:element name="titre" type="xsd:string" />
                        <xsd:element name="auteur" type="xsd:string" />
                        <xsd:element name="type" type="xsd:string" />
                        <xsd:element name="description" type="xsd:string" />
                        <xsd:element name="commentaire" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
                </xsd:sequence>
                <xsd:attribute name="ISBN" type="xsd:string" />
        </xsd:complexType>
</xsd:schema>

La méthode schemaValidate() de l’objet DOMDocument va nous permettre de valider le document.


adminbdd html # cat livre1.xml 
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE livre SYSTEM "livre.dtd">
<livre ISBN="2-99999-123-4" >
        <titre>Le XML rapide.</titre>
        <auteur>Auteur du livre</auteur>
        <type>Documentation technique</type>
        <description>Cet ouvrage explique les fondements du XML.</description>
        <commentaire>Bon rapport qualité prix.</commentaire>
        <commentaire>Bonne explication des bases.</commentaire>
</livre>

Créons ensuite un fichier PHP livreXSD.php, avec le code suivant :


adminbdd html # cat livreXSD.php 
<?php
        $document = DOMDocument::load("livre1.xml");
        echo ($document->schemaValidate("livre.xsd")) ? "Le document est valide." : "Le document n’est pas valide.";
?>

Après avoir testé dans mon navigateur, j'obtiens :


adminbdd html # php livreXSD.php 
Le document est valide.

65.3.2. Tests de validité

Comme dans l’exemple avec la DTD nous souhaitons maintenant rendre la description facultative.

Donc supprimez l’élément description du document et faites le test de validité. Vous obtenez alors un message d’erreur qui vous indique l’absence de l’élément. Pour régler ce problème, modifiez le schéma de la façon suivante :


adminbdd html # cat livre.xsd 
<?xml version='1.0' encoding='UTF-8' ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <xsd:element name="livre" type="livreType" />
        <xsd:complexType name="livreType">
                <xsd:sequence>
                        <xsd:element name="titre" type="xsd:string" />
                        <xsd:element name="auteur" type="xsd:string" />
                        <xsd:element name="type" type="xsd:string" />
                        <xsd:element name="description" type="xsd:string" minOccurs="0" />
                        <xsd:element name="commentaire" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
                </xsd:sequence>
                <xsd:attribute name="ISBN" type="xsd:string" />
        </xsd:complexType>
</xsd:schema>

L’ajout de l’attribut minOccurs="0" permet d’indiquer que l’élément est facultatif.

Enrichissons maintenant l’élément description en lui donnant un contenu mixte. Il pourra ainsi contenir du texte, un élément epoque et un élément resume. Nous apportons les modifications suivantes au document XML :

[adminbdd html]$ cat livre1.xml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE livre SYSTEM "livre.dtd">
<livre ISBN="2-99999-123-4" >
        <titre>Mon roman</titre>
        <auteur>Auteur du livre</auteur>
        <type>roman</type>
        <description>
                description du livre,
                <epoque>de nos jours</epoque>
                <resume>Resumé du roman</resume>
        </description>
</livre>

Si vous effectuez la validation après cette modification l’erreur qui remonte vous indique que l’élément est de type simple :

[adminbdd html]$ php livreXSD.php
PHP Warning:  DOMDocument::schemaValidate(): Element 'description': Element content is not allowed, because the type definition is simple. in /var/www/html/livreXSD.php on line 3
Warning: DOMDocument::schemaValidate(): Element 'description': Element content is not allowed, because the type definition is simple. in /var/www/html/livreXSD.php on line 3
Le document nest pas valide.

De fait s’il est de type simple, il ne peut pas contenir d’autres éléments. Nous modifions le schéma pour prendre ce point en considération :

[adminbdd html]$ cat livre.xsd
<?xml version='1.0' encoding='UTF-8' ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <xsd:element name="livre" type="livreType"/>
        <xsd:complexType name="livreType">
                <xsd:sequence>
                        <xsd:element name="titre" type="xsd:string"/>
                        <xsd:element name="auteur" type="xsd:string"/>
                        <xsd:element name="type" type="xsd:string"/>
                        <xsd:element name="description" minOccurs="0">
                                <xsd:complexType mixed="true">
                                        <xsd:sequence>
                                                <xsd:element name="epoque" type="xsd:string" minOccurs="0"/>
                                                <xsd:element name="resume" type="xsd:string" minOccurs="0"/>
                                        </xsd:sequence>
                                </xsd:complexType>
                        </xsd:element>
                        <xsd:element name="commentaire" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
                </xsd:sequence>
                <xsd:attribute name="ISBN" type="xsd:string"/>
        </xsd:complexType>
</xsd:schema>

Nous avons donc passé l’élément description en type complexe. L’attribut mixed="true" indique qu’il a un contenu mixte. Notez que les deux éléments enfants sont facultatifs.

[adminbdd html]$ php livreXSD.php
Le document est valide.

65.4. Document formation

Soit une formation composée de sessions qui elles-même comportent des apprenants. Ces apprenants ont un nom et un prénom et peuvent faire partie de différents groupes. Nous considérons le document suivant :

<formation>
	<session nom="01-2006">
		<apprenants>
			<apprenant identifiant="jdupond" password="jean">
				<nom>dupond</nom>
				<prenom>jean</prenom>
				<groupes>reseau information</groupes>
			</apprenant>
			<apprenant identifiant="mdurant" password="marie">
				<nom>durant</nom>
				<prenom>marie</prenom>
				<groupes>information developpement</groupes>
			</apprenant>
			<apprenant identifiant="pdujardin" password="pierre">
				<nom>dujardin</nom>
				<prenom>pierre</prenom>
				<groupes>reseau information</groupes>
			</apprenant>
			<apprenant identifiant="elamarche" password="eric">
				<nom>lamarche</nom>
				<prenom>eric</prenom>
				<groupes>developpement sécurité</groupes>
			</apprenant>
		</apprenants>
	</session>
</formation>

Exercice : Érivez la DTD et le schéma XML de ce document, vous pourrez vous aider des fonctions de validations que nous avons vues. Pour compléter le schéma vous pourrez considérer qu’une session ne peut pas avoir moins de trois apprenants et au maximum dix.

Voici les deux fichiers chargés de la définition du document :