L'utilisation du DOM de PHP 5 n'est pas particulièrement compliqué mais diffère des versions précédentes.

La création d'un document XML est le préalable obligatoire à l'exploitation du modèle d'objet.

$doc = new DOMDocument();

Le numéro de version XML et l'encodage de caractères peuvent être précisés lors de l'instanciation de la classe DOMDocument.

$doc = new DOMDocument('1.0', 'ISO-8859-1');
echo $doc->saveXML(); //Affiche le prologue

Désormais, l'objet DOMDocument est prêt à :

  • soit être construit de toutes pièces avec des éléments, attributs et textes,
  • soit être chargé à partir d'une source XML provenant d'un fichier ou d'une variable textuelle.
$doc->load('fichier.xml');
//ou
$xml = '<racine><element>texte</element></racine>';
$doc->loadXML($xml);

Un document HTML peut être chargé de la même façon avec les méthodes loadHTMLFile() et loadHTML().

L'exploration du document XML s'effectue en se déplaçant dans l'arborescence, de noeud en noeud, à l'aide des nombreuses méthodes et attributs proposées par le DOM.

Le premier noeud est obtenu par l'attribut documentElement de l'objet DOMDocument.

$racine = $doc->documentElement;

L'élément racine étant un descendant (héritage) d'un noeud, les méthodes et attributs de l'objet DOMNode sont utilisables pour récupérer des informations sur le noeud et sa descendance.

$nom = $racine->nodeName;
$valeur = $racine->nodeValue;
$type = $racine->nodeType;
$pere = $racine->nodeParent;
$liste_enfants = $racine->childNodes;
$attributs = $racine->attributes;
$document = $racine->ownerDocument;
$texte = $racine->textContent;

D'autres propriétés permettent d'accéder aux noeuds environnants.

$noeud = $liste_enfants->item(3);
$premier_enfant = $noeud->firstChild;
$dernier_enfant = $noeud->lastChild;
$grand_frere = $noeud->previousSibling;
$petit_frere = $noeud->nextSibling;

L'attribut childNodes est particulièrement utile pour parcourir rapidement et facilement une arborescence XML. Il suffit d'employer une boucle pour accéder à chaque enfant de la liste de noeuds obtenus.

$doc_xml = new DOMDocument();
$doc_xml->loadXML($source);
$racine = $doc_xml->documentElement;
echo '<h1>ELEMENT RACINE</h1>';
afficherInfos($racine);
$liste = $racine->childNodes;

echo '<h2>ENFANTS DE L\'ELEMENT RACINE</h2>';
foreach($liste as $noeud){
  afficherInfos($noeud);
}

function afficherInfos($noeud){
  $nom = $noeud->nodeName;
  $valeur = $noeud->nodeValue;
  $type = $noeud->nodeType;
  echo '<b>' . $nom . '</b> (' . $type . ') = ' . $valeur . '
'; }

Une boucle for pourrait également permettre de parcourir la liste en faisant appel à la méthode item() de l'objet DOMNodeList.

for($i = 0; $i < $liste.length; $i++){
  afficherInfos($liste.item($i));
}

Le parcours complet de l'arborescence demande une fonction récursive afin que chaque élément rencontré puisse être exploré à son tour, s'il possède des noeuds enfants (hasChildNodes()).

$doc_xml = new DOMDocument();
$doc_xml->loadXML($source);
$racine = $doc_xml->documentElement;
echo '<h1>ARBORESCENCE DE L\'ELEMENT RACINE</h1>';
parcourir($racine);

function afficherInfos($noeud){
  $nom = $noeud->nodeName;
  $valeur = $noeud->nodeValue;
  $type = $noeud->nodeType;
  echo $nom . '(' . $type . ') = "' . $valeur . '"<br/>';
}
function parcourir($noeud, $p = 0){
  if($p > 0) indenter($p);
  afficherInfos($noeud);
  if($noeud->nodeType == XML_ELEMENT_NODE 
  && $noeud->hasChildNodes()){
    $p++;
    $enfants = $noeud->childNodes;
    foreach($enfants as $enfant){
      parcourir($enfant, $p);
    }
  }
}
function indenter($n){
  $tab = '&nbsp;&nbsp;&nbsp;&nbsp;';
  for($i = 0; $i < $n; $i++){
    echo $tab;
  }
  echo ' - ';
}

Les attributs peuvent être récupérés de la même façon. Seuls les éléments XML contiennent des attributs. Fort de cette constatation, il suffit de tester si le noeud rencontré est un élément et qu'il possède des attributs (hasAttributes()). Puis avec l'attribut attributes de l'objet DOMNode, il faut récupérer la collection DOMNamedNodeMap et la parcourir.

function parcourir($noeud, $p = 0){
  if($p > 0) indenter($p);
  afficherInfos($noeud);
  if($noeud->nodeType == XML_ELEMENT_NODE 
  && $noeud->hasChildNodes()){
    if($noeud->hasAttributes()){
      $attributs = $noeud->attributes;
      indenter($p + 3);
      echo '[ ';
      foreach($attributs as $nom=>$valeur){
        echo '<b>'. $nom . '</b>="' . $valeur->value . '" ';
      }
      echo ']<br/>';
    }
    $p++;
    $enfants = $noeud->childNodes;
    foreach($enfants as $enfant){
      parcourir($enfant, $p);
    }
  }
}

Le noeud $valeur est un objet DOMAttr, c'est pourquoi il est nécessaire d'appeler l'attribut value pour obtenir la valeur du noeud.

Par ailleurs, plusieurs méthodes de l'objet DOMElement permettent de récupérer un attribut ou sa valeur, à l'aide de son nom et éventuellement son espace de noms. Il s'agît des méthodes getAttribute() et getAttributeNS() ou getAttributeNode() et getAttributeNodeNS() qui retournent respectivement la valeur de l'attribut ou un objet DOMAttr.

$noeuds = $doc_xml->getElementsByTagName('nom');
foreach($noeuds as $noeud){
  $langue = $noeud->getAttributeNode('langue');
  afficherInfos($langue);
  $nom = 'systeme_exploitation';
  $os = $noeud->getAttribute($nom);
  echo '<b>' . $nom .'</b> = "' . $os . '"';
}

L'objet DOMXpath et une expression XPath soumis à cet objet, fournit un moyen efficace pour acccéder directement à un noeud précis dans l'arborescence d'un document XML.

$doc_xml = new DOMDocument();
$doc_xml->loadXML($source);
$racine = $doc_xml->documentElement;
$xpath = new DOMXPath($doc_xml);
$noeuds = $xpath->query('//logiciel[1]/nom', $racine);
$noeud = $noeuds->item(0);
echo $noeud->nodeName . '="' . $noeud->nodeValue . '"';

Le second argument de la méthode query() détermine un noeud contextuel à partir duquel s'appliquera l'expression XPath spécifiée. Par défaut, l'expression XPath s'applique à la racine du document XML.

$logiciels = $doc_xml->getElementsByTagName('logiciel');
$xpath = new DOMXPath($doc_xml);
foreach($logiciels as $logiciel){
  $noeuds = $xpath->query('nom', $logiciel);
  $noeud = $noeuds->item(0);
  echo $noeud->nodeName . '="' . $noeud->nodeValue . '"';
}

La méthode getElementsByTagName() des objets DOMDocument et DOMElement retourne une liste de noeuds répondant au nom passé en argument.

$logiciels = $doc_xml->getElementsByTagName('logiciel');
foreach($logiciels as $logiciel){
  $nom = $logiciel->getElementsByTagName('nom');
  $commentaire = $logiciel->getElementsByTagName('commentaire');
  $editeur = $logiciel->getElementsByTagName('editeur');
  $prix = $logiciel->getElementsByTagName('prix');
}

Si le document possède des attributs ID permettant d'identifier sans ambiguités des éléments, il est possible d'employer la méthode getElementById() afin de récupérer l'élément correspondant à un identificateur passé en argument. Toutefois, le document XML doit être validé par une DTD, un schéma XML ou un schéma Relax NG.

$doc_xml = new DOMDocument();
$doc_xml->loadXML($source);
$doc_xml->validateOnParse = true;
$logiciel = $doc_xml->getElementById('XSY325684');
afficherInfos($logiciel);

Si vous supprimez la déclaration DOCTYPE de la source XML, la méthode getElementById() deviendra alors inopérante.

Lors du parcours des attributs d'un élément XML, la méthode DOMAttr->isId() permet de déterminer si l'attribut en cours est ou n'est pas un attribut identificateur. A l'instar de la méthode getElementById(), le document doit être validé.

$noeuds = $element->doc->getElementsByTagName('logiciel');
foreach($noeuds as $noeud){
  $id = $noeud->getAttributeNode('id');
  if($id->isId()){
    echo 'L'attribut est bien un identificateur !';
  }
}
<?php //Fichier contenant la source XML
$source = <<<XML
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE liste [
<!ELEMENT commentaire (#PCDATA)>
<!ELEMENT editeur (#PCDATA)>
<!ATTLIST editeur adresse CDATA #REQUIRED>
<!ELEMENT liste (logiciel+)>
<!ELEMENT logiciel (nom, commentaire, editeur, prix)>
<!ATTLIST logiciel id ID #REQUIRED>
<!ELEMENT nom (#PCDATA)>
<!ATTLIST nom 
      langue CDATA #REQUIRED 
      systeme_exploitation CDATA #REQUIRED>
<!ELEMENT prix (#PCDATA)>
<!ATTLIST prix monnaie CDATA #REQUIRED>
]>
<liste>
  <logiciel id="CTP0124555">
    <nom langue="US" systeme_exploitation="Win">
      Cooktop 2.200
    </nom>
    <commentaire>
      Un editeur XML, XSLT, XPath et DTD puissant et totalement gratuit.
    </commentaire>
    <editeur adresse="http://xmleverywhere.com/cooktop/">
      XML Everywhere
    </editeur>
    <prix monnaie="$US">00.00</prix>
  </logiciel>
  <logiciel id="XSY325684">
    <nom langue="US" systeme_exploitation="Win">
      XML Spy 4.1
    </nom>
    <commentaire>
      Un editeur XML desormais mature.
    </commentaire>
    <editeur adresse="http://www.xmlspy.com/default.html">
      Altova Inc.
    </editeur>
    <prix monnaie="$US">199,00</prix>
  </logiciel>
  <logiciel id="XSY210356">
    <nom langue="US" systeme_exploitation="Win">
      XML Spy 4.1 B2B Server
    </nom>
    <commentaire>
      La version 4 en version Business to business.
    </commentaire>
    <editeur adresse="http://www.xmlspy.com/default.html">
      Altova Inc.
    </editeur>
    <prix monnaie="$US">1 999,00</prix>
  </logiciel>
  <logiciel id="XWR387795">
    <nom langue="US" systeme_exploitation="Win">
      XMLwriter v1.21
    </nom>
    <commentaire>
      Permet de creer des documents XML.
    </commentaire>
    <editeur adresse="http://xmlwriter.net/">
      Wattle Software
    </editeur>
    <prix monnaie="$US">75,00</prix>
  </logiciel>
</liste>
XML;
?>