Ce matin, j'ai fait des recherches sur les façons de lire des fichiers XML en PHP et comme il y a plusieurs possibilités, je voulais en connaître les différences. Mon but était de lire et de traiter un fichier XML volumineux, soit le fichier iTunes de ma collection mp3, sauf que le XML n'était pas tout à fait comme je m'y attendais (c'est un PLIST; ouvrez-le vous allez comprendre pourquoi). J'ai fini par utiliser la classe iTunes XML parser pour faire le travail (elle utilise DomDocument à l'intérieur).
Pour en revenir aux tests que j'avais fait, j'ai quand même vu comment on pouvait les utiliser. J'ai quand même décidé de mettre en ligne mon exemple du même code, dans chacune de ses 3 variantes. Pour simplifier le problème que j'ai rencontré avec iTunes, j'ai cherché et téléchargé une liste quelconque, par exemple un fichier XML contenant plus de 2400 User-Agents (environ 700 kb). Peu importe si l'information est valide ou exacte, c'était suffisant pour tester les capacités des différents modèles.
La structure du fichier ressemble à ceci et mon but est d'extraire les nodes String qui contiennent le user-agent et d'en créer une liste :
<?xml version="1.0"?>XMLReader
<user-agents>
<user-agent>
<ID>id_moz_150408_3</ID>
<String>Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9b5) Gecko/2008032620 Firefox/3.0b5</String>
<Description>Mozilla Firefox 3.0 beta (Gran Paradiso) for Win</Description>
<Type>B</Type>
<Comment></Comment>
<Link1>http://developer.mozilla.org/en/docs/Firefox_3_for_developers</Link1>
<Link2></Link2>
</user-agent>
...
XMLReader est peut-être le plus compliqué à comprendre. On lit chaque node en boucle. Chacun possède un type (élément, attribut, texte, etc) qui doit être comparé avec les constantes prédéfinies. Ici, je recherche les éléments dont le nom est String.
$reader = new XMLReader();DOMDocument
$reader->open('allagents.xml');
while ($reader->read()) {
if ($reader->nodeType == XMLREADER::ELEMENT){
if ($reader->name == "String"){
$reader->read();
echo $reader->value . "\n";
}
}
}
$reader->close();
Celui-ci est un peu plus simple, principalement parce qu'on peut utiliser un sélecteur pour obtenir d'un seul coup tous les nodes du document qui portent le nom user-agent. Ensuite, il suffit de boucler sur l'array et d'extraire le contenu du node qui porte le nom String. La fonction getElementsByTagName() retourne dans un array tous les éléments du node user-agent qui portent ce nom. Il n'en contient qu'un seul, c'est pourquoi on doit y faire référence par item(0).
$doc = new DOMDocument();SimpleXML
$doc->load( 'allagents.xml' );
$nodes = $doc->getElementsByTagName( "user-agent" );
foreach($nodes as $ua) {
$string = $ua->getElementsByTagName( "String" );
echo $string->item(0)->nodeValue . "\n";
}
Encore mieux, SimpleXML rend l'opération plus facile. On peut utiliser xpath pour obtenir les nodes (au lieu de getElementsByTagName) en utilisant une syntaxe correspondant à la structure du fichier. Ensuite, on peut accéder aux propriétés de chaque node de la même façon qu'on le fait avec un objet (flèche).
$xml = new SimpleXMLElement('allagents.xml', Null, True);Ceci représente l'essentiel de la lecture XML. Le traitement peut être plus complexe selon le cas mais ça donne quand même un aperçu des alternatives. J'espère que j'ai pu vous aider à démêler tout ça et à choisir la forme la plus appropriée pour vos besoins.
// yeah! xpath!
$nodes = $xml->xpath('//user-agents/user-agent');
foreach($nodes as $ua) {
// attention, l'appel à String est case sensitive !
echo $ua->String . "\n";
}
Hello,
Juste pour noter qu'il y a une petite faute de frappe dans le code :
echo '$reader->value . '\n';
Le simple quote avant le $reader c'est pas bon :-)
D'ailleurs, les simples quotes pour le \n en principe ça ne marche pas, il faut des doubles pour qu'ils soient interprétés comme un saut de ligne.
Sinon, bon article mais il manque tout de même un petit bench pour comparer aussi l'exécution. Du coup je me le suis fait de mon côté parce que j'ai aussi besoin de choisir le meilleur moyen de parser du XML. Bon c'est pas forcément le bench fait dans les règles de l'art mais ça donne une bonne idée...
Résultat des courses :
En exécution pure, avec un microtime() avant et après :
SimpleXML arrive en tête avec 0,07 sec
XMLReader arrive ensuite avec 0,17 sec soit 2,18X plus que SimpleXML
DOMDocument arrive bon dernier avec 0,3sec soit presque 4X plus que SimpleXML !
J'ai également testé avec ab (Apache Bench) avec ce test : ab -n 500 -c 10
SimpleXML : 19,42 requêtes à la seconde
XMLReader : 11,16 requêtes à la seconde, presque deux fois moins (cohérent)
DomDocument : 1,08 requêtes à la seconde !! Les performances s'effrondent visiblement quand il y a des requêtes concurrentes... C'est 20X moins que SimpleXML... J'avoue j'ai eu la flemme d'attendre la fin du test, quand j'ai vu ce résultat j'ai recommencé en ne mettant que 50 requêtes à effectuer : même problème. Les performances remontent lorsqu'on baisse le nombre de concurrences, mais on reste loin de SimpleXML avec un petit 7 requêtes à la secondes lorsqu'il n'y a qu'une seule connection...
Tests effectués à l'arrache sur un iMac Alu 24" Core2Duo @2,4Ghz/2Go
DomDocument
Effectivement, tu as raison. Merci de m'avoir signalé l'erreur ainsi que pour les tests de benchmark.
Au départ, j'avais rédigé l'article en utilisant une liste UL/LI et pour une raison obscure, Blogger retirait les tags HTML à chaque fois dans le formatage du code. J'ai tout remplacé par des \n sans avoir testé à nouveau.
Merci beaucoup pour ce post!! J'etais justement coince pour la creation d'un site web ou je fais appel a un grand nombre de donnees et d'informations venant d'un fichier xml complet!
p.s: desoler pour le manque d'accent, mais j'ecris depuis un clavier qwerty...
Alors je choisis SimpleXML ;)