Parcou­rir des dossiers et filtrer les fichiers n’a jamais été aussi simple avec la SPL de PHP5

Parcou­rir les fichiers c’est simple avec PHP 5 et la SPL. Ou pas.

<?php
 class bigFileFilterIterator extends FilterIterator {
     public function accept() {
         $oFileInfo = $this->getInnerIterator()->current();
         return ($oFileInfo->getSize() > 10000);
     }
 }
 $themedir = __DIR__.'/theme';
 $iterator = new RecursiveDirectoryIterator($themedir, FilesystemIterator::SKIP_DOTS);
 $iterator->setFlags(FilesystemIterator::CURRENT_AS_FILEINFO);
 $recursiveIterator = new RecursiveIteratorIterator($iterator);
 foreach(new bigFileFilterIterator($recursiveIterator) as $file) {
     echo $file->getfilename()."n";
 }

Sérieu­se­ment ? Mais pourquoi ne puis-je pas utili­ser direc­te­ment le Recur­si­veDi­rec­to­ryI­te­ra­tor et dois-je instan­cier un Recur­si­veI­te­ra­torI­te­ra­tor ? Celui qui a conçu cette dernière classe souffre-t-il de bégaye­ment ? Rien que l’ins­tan­cia­tion du premier itéra­teur ne tient pas sur une seule ligne. Un ->getIn­nerI­te­ra­tor()->current() et pas un para­mètre direc­te­ment dans la méthode ->accept() ? Sérieu­se­ment ?

Montrer les nouvelles possi­bi­li­tés c’est appré­ciable, les quali­fier de simple est une insulte à l’in­tel­li­gence.

De mon temps on faisait quelque chose comme ce qui suit :

<?php
 function recursive_filter($path) {
     $dir = dir($path) ;
     while (false !== ($name = $dir->read())) {
         if ($name[0] === '.') return ;
         $new_path = $path.DIRECTORY_SEPARATOR.$name ;
         if (is_dir($new_path)) {
             recursive_filter($new_path) ;
         } elsif (file_size($new_path) > 10000) {
             echo $new_path ;
         }
     }
 }
 $themedir = __DIR__.'/theme';
 recursive_filter( $themedir ) ;

Ce n’était pas plus court, mais pas plus long. On peut entrer dans de longs discours pour savoir si c’était plus simple ou plus complexe, mieux struc­turé ou non, mais la valeur ajou­tée du nouveau code ne saute pas aux yeux côté simpli­cité je trouve.

À titre d’exemple, en ruby (« find » est dans la lib stan­dard) :

require 'find'
 theme_dir = File.dirname(__FILE__)."/theme"
 Find.find(theme_dir) do |path|
     next if FileTest.directory?(path)
     puts path if FileTest.size(path) > 10000
 end

Ou sur Python :

import os
 themedir = os.path.join(os.path.dirname(__file__), "theme")
 def find_files(directory):
   for root, dirs, files in os.walk(directory):
     for basename in files:
       if not file.startswith("."):
         filename = os.path.join(root, basename)
         yield filenames
 for filename in find_file(themedir)
   if os.path.getsize(filename) > 10000 :
     print filename

Il peut y avoir des fautes et il peut y avoir mieux dans les diffé­rents langages, et peu importe le nombre de lignes, mais dans les quatre codes précé­dents c’est bien le premier qui me semble complexe.

Il y a bien d’autres occa­sions de trou­ver PHP « simple », mais pas les itéra­teurs de la SPL.

5 commentaires

  1. En Perl, l’équivalent du code Ruby que tu as posté :

    use Path::Class;
    my $dir = dir(__FILE__, ‘theme’);
    while (my $file = $dir->next) {
    next if $file->is_dir;
    say $file if $file->stat->size > 10000;
    }

    Note:
    $dir et $file sont des objets répertoire et fichiers respectivement, qui ont la bonne idée de renvoyer le path quand ils sont interprété en tant que chaîne.

  2. Éric,

    Lorsque j’ai cliqué sur le lien de ton billet, je m’attendais à une critique du billet ayant servi de point de départ en bonne et due forme, mais à mon grand regret, ça n’a pas été le cas.
    En effet, prendre pour argent comptant le contenu d’un billet de blog pour ensuite faire du bashing facile est tout autant une insulte à l’intelligence, car le code qui t’a servi de base aurait dut être écrit de la manière suivante : https://gist.github.com/3944632

    Certes, cela reste verbeux et il est toujours possible de pester contre un certain nombre de choses (comme effectivement l’usage obligatoire de recursiveIteratorIterator qui me sort tout autant par les yeux que toi) mais cette version est tout de même nettement plus simple, plus lisible et au final pas mal éloignée de celle qui t’a servie de base pour ton argumentaire (et en prime, on peut passer des arguments à la méthode accept()).
    Certes, pour obtenir ce résultat, il faut avoir lu (ou avoir pris la peine de le faire…) la documentation de la SPL, en avoir compris les mécanismes et avoir une bonne connaissance des arguments par défaut des différentes méthodes, mais n’est ce pas le le travail de tout bon développeur ?
    Je sais que le bashing de PHP est à la mode, souvent avec raison mais aussi parfois à tort, et j’avoue être surpris que tu sois tombé dans facilité sur ce coup.

    1. je crois que j’ai été mal compris Frédéric. Je critique plus le billet de blog que PHP lui-même ici : Mon problème c’est montrer un code complexe et le qualifier de simple lors de la présentation.

      Les codes des autres langages ne sont là que pour montrer d’autres façons de faire, pas pour dire « PHP c’est nul ». Oui je trouve que la SPL a une tendance claire à sur-architecturer et copier les architectures Java, mais ce n’était pas le sujet ici. Si tu y vois du bashing PHP ça renforce plutôt mon discours.

      Ton code est effectivement beaucoup plus lisible. J’ai encore les deux points de mon premier paragraphe qui me sortent par les yeux (mais visiblement le premier te gène aussi) et qui me bloquent, mais si ça avait été présenté ainsi je n’aurai peut être pas fait de billet.

  3. Je pense que les comparaisons avec les autres langages m’ont induit en erreur, et du coup, j’ai sorti les crocs apparemment trop rapidement.
    Reste que la SPL, même si elle n’est pas exempte de défaut, bien au contraire, est un superbe outil qui permet d’écrire rapidement et simplement du code fiable pour des opérations courantes mais potentiellement complexes à coder (un parcours récursif n’est pas forcément la chose la plus évidente au monde), à la condition qu’elle soit correctement maîtrisée, ce qui n’est visiblement pas le cas de l’auteur du billet de départ.
    C’est un peu comme goto (pas taper), finaly (celui, je pense qu’il va poser quelques problèmes intéressants) ou les namespaces, ça permet de faire le pire comme le meilleur, en fonction des compétences du programmeur.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *