<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>php-experts.org - développement php et internet &#187; xml</title>
	<atom:link href="http://www.php-experts.org/tag/xml/feed" rel="self" type="application/rss+xml" />
	<link>http://www.php-experts.org</link>
	<description>Ressources sur le développement internet, PHP/MySQL, Ajax, marketing online, référencement...</description>
	<lastBuildDate>Sun, 27 Jun 2010 23:09:22 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Parser des XML volumineux avec XMLReader</title>
		<link>http://www.php-experts.org/developpement-web/php-developpement-web/parser-des-xml-volumineux-avec-xmlreader-42</link>
		<comments>http://www.php-experts.org/developpement-web/php-developpement-web/parser-des-xml-volumineux-avec-xmlreader-42#comments</comments>
		<pubDate>Fri, 28 Nov 2008 17:25:20 +0000</pubDate>
		<dc:creator>Didier</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[domdocument]]></category>
		<category><![CDATA[objet]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[simplexml]]></category>
		<category><![CDATA[xml]]></category>
		<category><![CDATA[xmlreader]]></category>

		<guid isPermaLink="false">http://www.php-experts.org/?p=42</guid>
		<description><![CDATA[Avec des classes comme DomDocument ou SimpleXML, le parsing de fichiers XML s&#8217;est considérablement simplifié en PHP 5. Mais quand on doit parser des fichiers très volumineux (comme j&#8217;ai eu à le faire récemment pour le catalogue produits de mon comparateur d&#8217;électroménager), on se retrouve très rapidement avec des scripts très gourmands en mémoire. En [...]]]></description>
			<content:encoded><![CDATA[<p>Avec des classes comme <strong>DomDocument</strong> ou <strong>SimpleXML</strong>, le parsing de fichiers XML s&#8217;est considérablement simplifié en PHP 5. Mais quand on doit parser des fichiers très volumineux (comme j&#8217;ai eu à le faire récemment pour le catalogue produits de mon <a href="http://www.achat-electromenager.org/">comparateur d&#8217;électroménager</a>), on se retrouve très rapidement avec des scripts très gourmands en mémoire. En effet, ces classes lisent et analysent la totalité du fichier XML avant de pouvoir agir dessus. Ce n&#8217;est pas le cas de <strong>XMLReader</strong>, qui elle, fonctionne en mode flux: le fichier est alors lu au fur et à mesure des besoins. Il en résulte des scripts plus rapides et très peu consommateurs de RAM.<br />
<span id="more-42"></span></p>
<h2>Utiliser XMLReader et SimpleXML de concert</h2>
<p>Voici comment on procède:</p>
<div class="dean_ch" style="white-space: wrap;">
<ol>
<li class="li1">
<div class="de1"><span class="re0">$xml</span> = <span class="kw2">new</span> XMLReader<span class="br0">&#40;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1"><span class="re0">$xml</span>-&gt;<span class="me1">open</span><span class="br0">&#40;</span><span class="st0">&#8216;gros_fichier.xml&#8217;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1"><span class="kw1">while</span><span class="br0">&#40;</span><span class="re0">$xml</span>-&gt;<span class="me1">read</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$xml</span>-&gt;<span class="me1">nodeType</span> == XMLREADER::<span class="me2">ELEMENT</span> &amp;amp;&amp;amp; <span class="re0">$xml</span>-&gt;<span class="me1">localName</span> == <span class="st0">&quot;product&quot;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$product</span> = <span class="re0">$xml</span>-&gt;<span class="me1">expand</span><span class="br0">&#40;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$product</span> = <span class="kw2">new</span> SimpleXMLElement<span class="br0">&#40;</span><span class="st0">&#8216;&lt;product&gt;&#8217;</span>.<span class="re0">$xml</span>-&gt;<span class="me1">readInnerXML</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="st0">&#8216;&lt;/product&gt;&#8217;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$prod</span><span class="br0">&#91;</span><span class="st0">&#8216;name&#8217;</span><span class="br0">&#93;</span> = <a href="http://www.php.net/utf8_decode"><span class="kw3">utf8_decode</span></a><span class="br0">&#40;</span><span class="re0">$product</span>-&gt;<span class="me1">name</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
<p>Ligne 1, on instancie classiquement un objet XMLReader. Le constructeur ne prend pas de paramètres. Ligne 2, on assigne le fichier que l&#8217;on souhaite parser.<br />
C&#8217;est ensuite que les joyeusetés commencent : $xml-&gt;read() permet de passer au node suivant. Tant que XMLReader trouvera des nodes, on continuera <strong>à lire le fichier séquentiellement</strong>. L&#8217;intérieur de la boucle contiendra le traitement que je souhaite effectuer sur chaque node. Ici, si le nodeType est &#8220;ELEMENT&#8221; si le localName (le libellé du tag) est &#8220;product&#8221;, nous sommes en présence de l&#8217;un des produits que je souhaite traiter.<br />
Dans ce cas, $xml-&gt;expand() permet de copier le node courant afin de le manipuler plus aisément. L&#8217;astuce ligne 6 (j&#8217;imagine bien qu&#8217;il doit y avoir un moyen de faire ça beaucoup plus proprement) consiste à recréer le node à partir de son localName et de la chaîne de caractères au format XML qu&#8217;il contient, récupérée grâce à $xml-&gt;readInnerXML(). Etant habitué à l&#8217;utilisation courante de SimpleXML, et pour des raisons de confort personnel, je passe la chaîne de caractères obtenue en paramètre au constructeur de SimpleXMLElement afin de récupérer un élement XML classique, que j&#8217;utilise ensuite simplement en accédant aux données qu&#8217;il contient.</p>
<h2>Vérifier le bon déroulement des opérations</h2>
<div class="dean_ch" style="white-space: wrap;">
<ol>
<li class="li1">
<div class="de1"><span class="re0">$i</span>++;</div>
</li>
<li class="li1">
<div class="de1"><span class="kw1">if</span> <span class="br0">&#40;</span><span class="br0">&#40;</span><span class="re0">$i</span> % <span class="nu0">500</span><span class="br0">&#41;</span> == <span class="nu0">0</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <a href="http://www.php.net/echo"><span class="kw3">echo</span></a> <span class="st0">&quot;$i &#8211; Ok<span class="es0">\r</span><span class="es0">\n</span>&quot;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <a href="http://www.php.net/flush"><span class="kw3">flush</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
<p>Pour les imports de gros fichiers (1.2 giga-octets dans mon cas), mieux vaut savoir exactement ce qu&#8217;il se passe. J&#8217;ai donc déclaré avant mon while une variable $i=0, que j&#8217;incrémente à la fin de ma boucle, après avoir traité un produit. Ligne 2, je vérifie que $i (le nombre de produits traités) est un multiple de 500. Si oui, j&#8217;affiche le nombre courant et le flush() permet d&#8217;envoyer immédiatement le contenu du buffer à la sortie du script. Ici, le but était de faire tourner le script en ligne de commandes (CLI), mais cela aurait aussi bien marché avec un navigateur. (Il aurait fallu remplacer les \r\n par un &lt;br /&gt; classique.<br />
<br />
Si cet article vous a intéressé, j&#8217;apprécierais un peu de promotion : <img src='http://www.php-experts.org/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p><script>scoopeo_url='http://www.php-experts.org/developpement-web/php-developpement-web-2/parser-des-xml-volumineux-avec-xmlreader-42'</script><script language='javascript' src='http://scoopeo.com/clicker/insert/small'></script></p>
<hr />
]]></content:encoded>
			<wfw:commentRss>http://www.php-experts.org/developpement-web/php-developpement-web/parser-des-xml-volumineux-avec-xmlreader-42/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
