mageekblog - Mot-clé - SPLLe blog personnel de Frédéric Hardy. Au menu, PHP, agilité, FreeBSD, cuisine et photographies.2021-12-02T08:20:54+01:00Frédéric Hardyurn:md5:26874ca5b8cd4cac8d08b0e68e64f63aDotclearParangonnons encore un peu !urn:md5:4e8c3f2e2759391bdea707f9c2e7f1472010-04-09T09:30:00+02:002010-06-09T18:29:25+02:00mageekguyPHPfilterIteratorforeachPHPSPL<p>Cette nuit, les jumeaux ont décidé de donner de la voix.</p>
<p>Du coup, j'ai eu le temps de gamberger sur les remarques qui ont été faites en commentaire <a href="http://blog.mageekbox.net/?post/2010/04/07/Parangonnons-un-peu-%21">de mon précédent billet</a>.</p>
<p>J'ai donc optimisé mon code <a href="http://www.php.net">PHP</a>, afin de voir si le différentiel entre la mise en oeuvre de la combinaison de l'instruction <a href="http://fr.php.net/manual/en/control-structures.foreach.php"><code>foreach</code></a> et de la fonction <a href="http://fr.php.net/is_string"><code>is_string()</code></a> et l'utilisation de la classe <a href="http://www.php.net/%7Ehelly/php/ext/spl/protocolFilterIterator-p.html"><code>filterIterator</code></a> de la <a href="http://fr2.php.net/spl"><abbr title="Standard Php Library">SPL</abbr></a> était induite par mon algorithme ou par la lenteur de l'itérateur.</p> <p>J'ai donc modifié l'implémentation de ma classe dérivant de <a href="http://www.php.net/%7Ehelly/php/ext/spl/protocolFilterIterator-p.html"><code>filterIterator</code></a> :</p>
<blockquote><pre><code>...<br />class keyAsStringFilterIterator extends FilterIterator<br />{<br /> protected $stringFound = true;<br /><br /> public function __construct(array $array)<br /> {<br /> asort($array);<br /> parent::__construct(new arrayIterator($array));<br /> }<br /><br /> public function rewind()<br /> {<br /> parent::rewind();<br /> $this->stringFound = true;<br /> }<br /><br /> public function valid()<br /> {<br /> return ($this->stringFound === true && parent::valid() === true);<br /> }<br /><br /> public function accept()<br /> {<br /> if (is_string($this->key()) === false)<br /> {<br /> $this->stringFound = false;<br /> }<br /><br /> return $this->stringFound;<br /> }<br />}<br />...<br /></code></pre></blockquote>
<p>Les plus attentifs auront remarqué que les <code><a href="http://blog.pascal-martin.fr/post/php-5.3-1-closures-et-lambdas">closures</a></code> ne sont plus utilisées, puisque l'itérateur est maintenant très fortement spécialisé, d’où son changement de nom.</p>
<p>Afin de ne pas pénaliser l'approche <q>traditionnelle</q>, j'ai également modifié son code de la manière suivante :</p>
<blockquote><pre><code>...<br />asort($array);<br /><br />foreach ($array as $key => $value)<br />{<br /> if (is_string($key) === true)<br /> {<br /> echo $value;<br /> }<br /> else<br /> {<br /> break;<br /> }<br />}<br />...<br /></code></pre></blockquote>
<p>Et les résultats, alors ?</p>
<p>Et bien j'aurais mieux fait d'essayer de dormir plutôt que de réfléchir à ce problème :</p>
<blockquote><pre><code>fch@diablo:~/tmp<br />37> php -nf bench.php <br />Iterator: abcdefghijklmnopqrstuvwxyz [0.64576697349548]<br />Foreach: abcdefghijklmnopqrstuvwxyz [0.26274108886719]<br />Delta: 0.3830258846283<br />Ratio: 2.4578073276613<br /></code></pre></blockquote>
<p>L'itérateur reste plus de deux fois plus lent que la solution classique.</p>
<p>Vous me direz que c'est toujours mieux que d'être 7 fois plus lent et vous n'aurez pas tort.</p>
<p>Et comme quoi il n'est pas inutile d'essayer d'optimiser ses algorithmes, même si ca ne change pas la face du monde, cela permet de gagner de précieuse seconde.</p>
<p>Et curieusement, en chargeant mes extensions, le delta n'est plus deux fois mais bien huit fois plus important :</p>
<blockquote><pre><code>fch@diablo:~/tmp<br />40> php -f bench.php <br />Iterator: abcdefghijklmnopqrstuvwxyz [2.0768599510193]<br />Foreach: abcdefghijklmnopqrstuvwxyz [0.24072194099426]<br />Delta: 1.836138010025<br />Ratio: 8.6276304621056<br /></code></pre></blockquote>
<p>Du coup, j'ai cette fois pris la peine d'investiguer, et l'extension <a href="http://xdebug.org/">xdebug</a> est bien la responsable de ce phénomène puisqu'en la désactivant, je retrouve des résultats beaucoup plus similaires à ceux que j’obtiens avec les extensions désactivées.</p>
<p>Pour ceux qui veulent s'amuser, le code source complet est attaché à ce billet, et les tests ont été effectués dans les même conditions que dans le cadre <a href="http://blog.mageekbox.net/?post/2010/04/09/../?post/2010/04/07/Parangonnons-un-peu-%21">de mon précédent billet</a>.</p>
<p>Et cela n'a rien à voir avec ce qui précède, mais avec la publication de celui-ci, ce blog compte plus de 100 billets au compteur.</p>http://blog.mageekbox.net/?post/2010/04/09/Parangonnons-encore-un-peu-%21#comment-formhttp://blog.mageekbox.net/?feed/atom/comments/107Parangonnons un peu !urn:md5:470abd9c121f42c2d3e64a6050bc96cd2010-04-08T08:15:00+02:002010-06-09T18:30:08+02:00mageekguyPHPbenchfilterIteratorforeachis_stringPHPPHP XSPL<p>Dans le cadre de l'un de mes projets, j'ai été amené à extraire d'un <a href="http://fr2.php.net/manual/en/language.types.array.php">tableau PHP</a> les éléments ayant pour clef une chaîne de caractères.</p>
<p>Mon premier réflexe a été de me tourner vers la fonction <a href="http://fr.php.net/manual/en/function.array-filter.php"><code>array_filter()</code></a>, mais malheureusement, elle ne permet pas de filtrer sur les clefs d'un tableau.</p>
<p>Du coup, j'ai été obligé de trouver une autre stratégie.</p>
<p>J'ai d'abord envisagé un recours très classique à l'instruction <a href="http://fr.php.net/manual/en/control-structures.foreach.php"><code>foreach</code></a> et à la fonction <a href="http://fr.php.net/is_string"><code>is_string()</code></a>, avant d'envisager sérieusement l'utilisation d'un objet dérivé la classe <a href="http://www.php.net/%7Ehelly/php/ext/spl/protocolFilterIterator-p.html"><code>filterIterator</code></a> de la <a href="http://fr2.php.net/spl"><abbr title="Standard Php Library">SPL</abbr></a>.</p>
<p>Cependant, vu que je ne savais pas quelle méthode serait la plus efficace, et que je souhaitais depuis longtemps me faire une idée des performances de cette classe de la <a href="http://fr2.php.net/spl"><abbr title="Standard Php Library">SPL</abbr></a>, je me suis fendu d'un petit <del>benchmarking</del> <a href="http://fr.wikipedia.org/wiki/Benchmarking">étalonnage</a>, et vu que je suis d'un naturel joueur, j'ai décidé d'ajouter les <code><a href="http://blog.pascal-martin.fr/post/php-5.3-1-closures-et-lambdas">closures</a></code> dans l'équation.</p> <p>J'ai donc écrit le code suivant :</p>
<blockquote><pre><code>closure = $closure;<br /> }<br /><br /> public function accept()<br /> {<br /> return ($this->closure->__invoke($this) === true);<br /> }<br />}<br /><br />$letters = str_split('abcdefghijklmnopqrstuvwxyz');<br />$array = array_combine($letters, $letters) + range(1, 100000);<br /><br />echo 'Iterator: ';<br /><br />$start = microtime(true);<br /><br />foreach (new closureFilterIterator(new arrayIterator($array), function(\iterator $iterator) { return is_string($iterator->key()); }) as $key => $value)<br />{<br /> echo $value;<br />}<br /><br />$time1 = (microtime(true) - $start);<br /><br />echo ' [' . $time1 . ']' . "\n";<br /><br />echo 'Foreach: ';<br /><br />$start = microtime(true);<br /><br />foreach ($array as $key => $value)<br />{<br /> if (is_string($key) === true)<br /> {<br /> echo $value;<br /> }<br />}<br /><br />$time2 = (microtime(true) - $start);<br /><br />echo ' [' . $time2 . ']' . "\n";<br /><br />echo 'Delta: ' . ($time1 - $time2) . "\n";<br />echo 'Ratio: ' . ($time1 / $time2) . "\n";<br /><br />?></code></pre></blockquote>
<p>Rien de bien particulier, si ce n'est l'utilisation de la méthode magique __invoke() pour appeler la closure vu que <del>PHP c'est de la merde</del> qu'il n'est pas possible d'appeler une <code><a href="http://blog.pascal-martin.fr/post/php-5.3-1-closures-et-lambdas">closure</a></code> de la manière normale si cette dernière est une propriété d'objet.</p>
<p>Pour votre information, <a href="http://bugs.php.net/bug.php?id=51326">j'ai entendu dire</a> que ce point serait amélioré dans la prochaine version du langage, même si je n'ai pas l'ombre d'une confirmation de cela pour le moment.</p>
<p>Et les résultats, alors ?</p>
<p>Ils ont été sans appel.</p>
<p>L’exécution du code ci-dessus sur ma machine de développement en ligne de commande m'a donné les résultats suivant :</p>
<blockquote><pre><code>fch@diablo:~/tmp<br />8> php -nf closureFilterIterator.php <br />Iterator: abcdefghijklmnopqrstuvwxyz [0.39656400680542]<br />Foreach: abcdefghijklmnopqrstuvwxyz [0.055746793746948]<br />Delta: 0.34081721305847<br />Ratio: 7.113664843319<br /><br /></code></pre></blockquote>
<p>La méthode qui met en oeuvre <a href="http://www.php.net/%7Ehelly/php/ext/spl/protocolFilterIterator-p.html"><code>filterIterator</code></a> est donc 7 fois plus lente que celle utilisant l'instruction <a href="http://fr.php.net/manual/en/control-structures.foreach.php"><code>foreach</code></a> et la fonction <a href="http://fr.php.net/is_string"><code>is_string()</code></a><code>.</code></p>
<p>Nous pourrions nous arrêter là, mais les plus attentifs auront remarqué l'utilisation du paramètre <code>-n</code>, qui permet de ne pas charger en mémoire les modules additionnels de <a href="http://www.php.net">PHP</a>, ceci afin d'éliminer l'influence d'extension tel qu'<a href="http://php.net/manual/fr/book.apc.php">APC</a> ou <a href="http://xdebug.org/">xdebug</a>.</p>
<p>Si je n'utilise pas ce paramètre, j'obtiens les résultats suivant :</p>
<blockquote><pre><code>fch@diablo:~/tmp<br />9> php -f closureFilterIterator.php <br />Iterator: abcdefghijklmnopqrstuvwxyz [2.8110311031342]<br />Foreach: abcdefghijklmnopqrstuvwxyz [0.51954388618469]<br />Delta: 2.2914872169495<br />Ratio: 5.4105748867091<br /></code></pre></blockquote>
<p>Oui, vous avez bien lu, bizarrement, non seulement les deux méthodes prennent plus de temps, mais le delta entre les deux est moins significatif...</p>
<p>J'avoue ne pas avoir pris le temps de chercher la ou les extensions responsables de ce phénomène, même si je pense très fortement que le coupable est <a href="http://xdebug.org/">xdebug</a>.</p>
<p>Conclusion, mettre en oeuvre la <a href="http://fr2.php.net/spl"><abbr title="Standard Php Library">SPL</abbr></a> n'est pas forcément la meilleure de choses à faire en terme de performance, et charger en mémoire des modules <a href="http://www.php.net">PHP</a> non indispensables au fonctionnement du code en production n'est pas non plus une bonne idée.</p>
<p>Mais bon, la <a href="http://fr2.php.net/spl"><abbr title="Standard Php Library">SPL</abbr></a>, cela permet d'écrire quand même du bien joli code...</p>
<p>Pour <del>ceux qui veulent se la mesurer</del> les fans de comparaisons, le fichier contenant le code est attaché à ce billet et voici quelques informations complémentaires :</p>
<blockquote><pre><code>fch@diablo:~/tmp<br />10> uname -a<br />FreeBSD diablo.local 7.1-RELEASE-p9 FreeBSD 7.1-RELEASE-p9 #9: Thu Dec 10 13:41:18 CET 2009 root@diablo.local:/usr/obj/usr/src/sys/DIABLO i386</code></pre></blockquote>
<blockquote><pre><code>fch@diablo:~/tmp<br />11> php --version<br />PHP 5.3.1 with Suhosin-Patch (cli) (built: Dec 10 2009 17:57:41) <br />Copyright (c) 1997-2009 The PHP Group<br />Zend Engine v2.3.0, Copyright (c) 1998-2009 Zend Technologies<br /> with Xdebug v2.0.5, Copyright (c) 2002-2008, by Derick Rethans<br /> with Suhosin v0.9.29, Copyright (c) 2007, by SektionEins GmbH</code></pre></blockquote>
<blockquote><pre><code>fch@diablo:~/tmp<br />12> php -m<br />[PHP Modules]<br />bcmath<br />bz2<br />calendar<br />Core<br />ctype<br />curl<br />date<br />dom<br />ereg<br />exif<br />expect<br />fileinfo<br />filter<br />ftp<br />gd<br />gettext<br />gmp<br />hash<br />iconv<br />imagick<br />imap<br />json<br />ldap<br />libxml<br />mbstring<br />mcrypt<br />mdbtools<br />memcache<br />mhash<br />mssql<br />mysql<br />mysqli<br />mysqlnd<br />odbc<br />openssl<br />pcntl<br />pcre<br />pdf<br />PDO<br />pdo_mysql<br />pdo_sqlite<br />pgsql<br />posix<br />pspell<br />readline<br />recode<br />Reflection<br />session<br />shmop<br />SimpleXML<br />snmp<br />soap<br />sockets<br />SPL<br />SQLite<br />standard<br />suhosin<br />svn<br />sysvmsg<br />sysvsem<br />sysvshm<br />tidy<br />tokenizer<br />wddx<br />xdebug<br />xml<br />xmlreader<br />xmlrpc<br />xmlwriter<br />xsl<br />yaz<br />zip<br />zlib<br /><br />[Zend Modules]<br />Xdebug</code></pre></blockquote>
<p>J'ajouterais que contrairement aux apparences, ce billet n'est sponsorisé ni par l'<a href="http://www.academie-francaise.fr/">Académie Française</a>, ni par la <a href="http://www.culture.gouv.fr/culture/dglf/"><abbr title="Délégation Générale à la Langue Française et aux Langues de France">DGLFLF</abbr></a>.</p>http://blog.mageekbox.net/?post/2010/04/07/Parangonnons-un-peu-%21#comment-formhttp://blog.mageekbox.net/?feed/atom/comments/106Erreur ou exception ?urn:md5:4955a32384f9adfcce9f7b06b670976d2010-03-11T09:49:00+01:002010-05-09T14:29:58+02:00mageekguyPHPerreurexceptionPHPSPL<p>L'un des apports majeurs de <a href="http://www.php.net">PHP</a> 5.0 a été la gestion des <a href="http://fr.php.net/manual/fr/class.exception.php">exceptions</a>.</p>
<p>Du coup, pendant un temps, la mode a été de transformer les erreurs <q>classiques</q> qu'il est possible de générer à l'aide de la fonction <a href="http://fr.php.net/manual/fr/function.trigger-error.php"><code>trigger_error()</code></a> en exception, voir même de définir une fonction spécifique de gestion des erreurs à l'aide de <a href="http://fr.php.net/manual/fr/function.set-error-handler.php%22"><code>set_error_handler()</code></a> pour <q>attraper</q> les erreurs générées par PHP ou à l'aide de <a href="http://fr.php.net/manual/fr/function.trigger-error.php"><code>trigger_error()</code></a> afin de les transformer en exception.</p>
<p>Si cette folie <del>furieuse</del> semble être passée, il en reste encore des traces, et il arrive bien souvent que je sois amené à voir du code qui, à mon sens, devrait générer une erreur au lieu d'une exception.</p> <p>En effet, je pense qu'une exception, comme son nom l'indique, doit être exceptionnelle, et que de plus, une exception et une erreur ne s'adresse pas du tout aux mêmes entités.</p>
<p>Les <a href="http://fr.php.net/manual/fr/class.exception.php">exceptions</a> ne devrait être misent en oeuvre que lorsqu'un événement qui n'est pas censé se produire survient lors de l'éxécution du code, comme par exemple l'impossibilité d'écrire sur le système de fichiers, de se connecter à une base de données, j'en passe et des meilleures, afin de permettre la mise en oeuvre d'une solution alternative permettant de contourner ou solutionner le problème rencontré, quitte à dégrader la qualité de service offerte par le code.</p>
<p>L'exception est effectivement le moyen le plus pratique et élégant pour pallier à l'indisponibilité de la base de données, en permettant de mettre en oeuvre une alternative de manière très simple au niveau du code, par exemple de la manière suivante :</p>
<blockquote><pre><code><?php<br />...<br />try<br />{<br /> $mysql->connect();<br />}<br />catch (databaseException $exception)<br />{<br /> $logger->wrtie($exception->getMessage());<br /> header('Status: 303');<br /> header('Location: http://static.mondomaine.com');<br />}<br />...<br />?><br /></code></pre></blockquote>
<p>Cet exemple permet de se rendre compte que le code est le destinataire et le gestionnaire de l'exception, et non le développeur, qui n'a pas son mot à dire lorsque survient l'exception, même si c'est effectivement lui qui a décidé du comportement du code en cas d'exception lors de la phase de conception.</p>
<p>Pour résumé, les exceptions sont donc destinées à être mise en oeuvre lors de l'éxécution du code et leur gestion est assurée par ce dernier, au contraire des erreurs, qui doivent être à mon sens utilisées lors de la conception du code et qui sont à destination du développeur.</p>
<p>En effet, les erreurs doivent servir à avertir le programmeur qu'il n'utilise pas correctement une fonction ou une méthode, qu'elle soit native ou externe au langage, bref, qu'il n'utilise pas le code correctement.</p>
<p>Typiquement, j'utilise les erreurs de la manière suivante :</p>
<blockquote><pre><code><?php<br />... <br />public function setWith(array $arguments)<br />{<br /> if (array_key_exists(0, $arguments) === false)<br /> {<br /> trigger_error('Argument must be set at index 0', E_USER_ERROR);<br /> }<br /> else<br /> {<br /> $this->value = $arguments[0];<br /> return parent::setWith($arguments);<br /> }<br />}<br />...<br />?><br /></code></pre></blockquote>
<p>Dans ce cas, la cible de l'erreur est clairement le développeur, et c'est à lui, lors de la phase de conception et de test de son code, de prendre les mesures adéquates pour régler le problème, le code ne pouvant se corriger de lui-même.</p>
<p>Cependant, tout cela est sujet à discussion, et reléve énormément de la bataille de chapelle.</p>
<p>L'important, comme dans le cas des conventions de nommage, est de définir les cas d'utilisation respectifs des erreurs et des exceptions le plus tôt possible, de s'y tenir, et de communiquer largement à ce sujet, afin que tous les intervenants sur le code sachent précisement la solution à utiliser en fonction des différents cas de figure.</p>
<p><a href="http://www.php.net/">PHP</a> apporte d'ailleurs à ce sujet son aide, et cela de manière native, en laissant clairement le choix au développeur d'utiliser l'une ou l'autre des philosophies.</p>
<p>La <a href="http://www.php.net/%7Ehelly"><abbr title="Standard PHP Library">SPL</abbr></a> implémente en effet un certain nombre d'exceptions dérivant du type de base <code>Exception</code>, séparée en deux familles distinctes :</p>
<ol><li>L'une, composée des dérivées de <a href="http://www.php.net/%7Ehelly/php/ext/spl/classRuntimeException.html"><code>RuntimeException</code></a>, est spécialement dédiée à la gestion des problèmes survenant lors de l'éxécution du code et il s'agit donc de celle qui a ma préférence.</li>
<li>L'autre, composées des dérivées de <a href="http://www.php.net/%7Ehelly/php/ext/spl/classLogicException.html"><code>LogicException</code></a>, doit plus particulièrement être mise en oeuvre pour gérer les problèmes liés à la conception et la mise en oeuvre du code.</li>
</ol>
<p>Cependant, comme à son habitude, <a href="http://www.php.net">PHP</a> fait aussi <del>de la merde</del> n'importe quoi puisqu'il arrive souvent qu'il génére des erreurs là ou des exceptions seraient beaucoup plus appropriées.</p>
<p>Malheureusement, la sacro-sainte compatibilité avec les anciennes versions fait qu'il en sera toujours ainsi...</p>
<p>Et vous, vous utilisez les erreurs et les exceptions de quelle manière ?</p>http://blog.mageekbox.net/?post/2010/03/11/Erreur-ou-exception#comment-formhttp://blog.mageekbox.net/?feed/atom/comments/94La SPL, c'est bien... ou pas !urn:md5:44bc9c063e91e0750587cb8e6b3f74fb2008-11-25T00:00:00+01:002008-11-26T12:14:11+01:00mageekguyPHPPHPSPL<p>La <a href="http://www.php.net/%7Ehelly"><acronym title="Standard PHP Library">SPL</acronym></a> est une des bonnes choses qui sont arrivées avec la version 5.1 du langage <a href="http://www.php.net">PHP</a>.</p>
<p>Elle permet en effet entre autre l'utilisation des itérateurs</p>
<p>Ainsi, il devient possible d'itérer sur un objet dès lors que sa classe implémente l'interface <code>iterator</code> ou <code>iteratorAggregate</code>.</p> <p>Grâce à ce mécanisme, il est possible de faire des choses de ce style :</p>
<pre><code><br />class myDirectory implements iterator<br />{<br /> ...<br />}<br /><br />$directory = new myDirectory('/tmp');<br />foreach ($directory as $inode)<br />{<br /> echo $inode;<br />}<br /></code></pre>
<p>Cerise sur le gâteau, il est également possible de filtrer ce qui est renvoyé par un objet de la classe <code>iterator</code> à l'aide de l'itérateur <code>filterIterator</code>, de la manière suivante :</p>
<pre><code>class myDirectoryFilter extends filterIterator<br />{<br /> ...<br />}<br /><br />$directory = new myDirectory('/tmp');<br />foreach (new myDirectoryFilter($directory) as $inode)<br />{<br /> echo $inode;<br />}<br /></code></pre>
<p>C'est évidement très puissant.</p>
<p>Cependant, dans certain cas, il y a un problème.</p>
<p>Nous allons par exemple ajouter dans la classe <code>myClass</code> une méthode <code>getFromDirectory</code> qui prend en argument un objet de la classe <code>myDirectory</code> et dont le but est d'effectuer un traitement, peu importe lequel, sur chaque fichier renvoyé par cet objet.</p>
<p>Son code est le suivant :</p>
<pre><code>class myClass<br />{<br /> public function getFromDirectory(myDirectory $directory)<br /> {<br /> if ($directory->exists())<br /> {<br /> foreach ($directory as $inode)<br /> {<br /> ...<br /> }<br /> }<br /> }<br />}<br /></code></pre>
<p>Tout va bien, sauf si nous souhaitons laisser au développeur la possibilité de filter ce que renvoie l'itérateur <code>myDirectory</code>, indépendament de cet itérateur.</p>
<p>En effet, notre définition de la méthode oblige l'argument à être de la classe <code>myDirectory</code> et non de la classe <code>filterIterator</code>.</p>
<p>Qu'à cela ne tienne, nous allons définir un constructeur qui permet la définition du filtre à employer, et nous allons modifier la méthode <code>myClass::getFromDirectory()</code> de la manière suivante :</p>
<pre><code>class myClass<br />{<br /> protected $filter = null;<br /><br /> public function __construct(filterIterator $filter)<br /> {<br /> $this->setFilter($filter);<br /> }<br /><br /> public function setFilter(filterIterator $filter)<br /> {<br /> $this->filter = $filter;<br /> return $this;<br /> }<br /><br /> public function getFromDirectory(myDirectory $directory)<br /> {<br /> if ($directory->exists())<br /> {<br /><br /> $this->filter->setInnerIterator($directory);<br /> foreach ($this->filter as $inode)<br /> {<br /> ...<br /> }<br /> }<br /> }<br /> <br /> ...<br />}<br /><br /></code></pre>
<p>Sauf que cela n'est pas possible de par la conception de la classe <code>filterIterator</code>...</p>
<p>En effet, son constructeur impose que son argument soit un objet de la classe <code>iterator</code>.</p>
<p>Cela revient donc à dire qu'il faut connaître à l'avance ce qui doit être filtré, ce qui n'est pas notre cas.</p>
<p>De plus, la méthode <code>filterIterator::setInnerIterator()</code> n'existe pas.</p>
<p>Cerise sur le gâteau, la propriété <code>filterIterator::$innerIterator</code> est une propriété privée de la classe, ce qui fait qu'il n'est pas possible de contourner le problème en dérivant <code>filterIterator</code>.</p>
<p>Quelle est la solution, alors ?</p>
<p>Modifier notre définition de <code>myClass::getFromDirectory()</code> pour qu'elle accepte un objet de la classe <code>filterIterator</code> ? Ce n'est pas jouable, puisque nous voulons pouvoir utiliser des méthodes appartenant à la classe <code>myDirectory</code> dans cette méthode, en l'occurence <code>myDirectory::exists()</code>.</p>
<p>Modifier la classe <code>myDirectory</code> pour y encapsuler le filtrage ? Mauvaise idée, car pour chaque filtrage, il faudrait dériver la classe <code>myDirectory</code>.</p>
<p>Je me suis finalement résigné à écrire ma propre classe afin de faire ce filtrage, en me basant sur le <a href="http://www.php.net/%7Ehelly/php/ext/spl/filteriterator_8inc-source.html">code source</a> de <code>filterIterator</code> afin de pouvoir découpler complétement le filtrage de l'itération.</p>
<p>J'ai ainsi rajouté une méthode <code>myFilterIterator::setInnerIterator()</code> et j'ai modifié la signature du constructeur pour qu'il accepte la valeur <code>null</code> par défaut.</p>
<p>J'ai donc dupliqué au moins 90% du code de <code>filterIterator</code>, ce qui nous éloigne des bonnes pratiques... mais après tout, comme me l'a fait remarquer <a href="http://www.durcommefaire.net">Jean-Marc</a>, déclarer une propriété d'une classe pouvant être dérivée comme privée ne fait déjà pas partie des bonnes pratiques.</p>
<p>Mais si par hasard, vous avez une solution qui permette de découpler le filtrage de l'itération dans le cadre de cette problèmatique, en utilisant les outils natifs de la <a href="http://www.php.net/%7Ehelly"><acronym title="Standard PHP Library">SPL</acronym></a>, j'en serais très heureux, et peut être que cela me fera penser moins souvent, que <a href="http://www.php.net">PHP</a>, c'est...</p>
<p>Et si, en plus, vous savez comment faire remonter ce genre de problème à l'<a href="http://marcus-boerger.de">auteur</a> de la <a href="http://www.php.net/%7Ehelly"><acronym title="Standard PHP Library">SPL</acronym></a>, j'en serais encore plus heureux, car le <q><a href="http://bugs.php.net">bug tracker</a></q> ne semble pas être le meilleur endroit pour ce genre de choses.</p>http://blog.mageekbox.net/?post/2008/11/07/La-SPL%2C-c-est-bien...-ou-pas-%21#comment-formhttp://blog.mageekbox.net/?feed/atom/comments/6