Parcourir des dossiers et filtrer les fichiers n'a jamais été aussi simple avec la SPL de PHP5

Un autre grand classique lors de la création d'un site, c'est le parcours des dossiers du disque dur à la recherche de fichiers. Depuis PHP 5.3, la SPL s'est enrichie de classes permettant de parcourir les dossiers et d'ajouter des filtres pour ne sélectionner que certains fichiers. Dans cet article je vous propose des exemples basiques pour comprendre le fonctionnement des classes PHP de la SPL.

Imaginons cette structure de dossier et les fichiers comme base pour les exemples:

_ theme
    |__ css
             |__ front.css
             |__ back.css
    |__  images
             |__ header.jpg
             |__ logo.png
             |__ rss_tag.png
             |__ mainmenu.jpg

1. Lire tous les fichiers d'un dossier

Le parcours d'un dossier est très simple à mettre en oeuvre avec la classe FilesystemIterator. il suffit d'instancier la classe en donnant le chemin du dossier à parcourir puis d'utiliser foreach pour boucler sur chaque élément du dossier.

<?php
 
$themedir = __DIR__.'/theme';
$cssdir = $themedir.'/css';
$imagesdir = $themedir.'/images';
 
$iterator = new FilesystemIterator($themedir, FilesystemIterator::SKIP_DOTS);
 
foreach($iterator as $file) {
    echo $file->getfilename()."\n";
}
 

La variable $file est une instance de l'objet splFileInfo. Le résultat de cette fonction va imprimer css et images. Pour lister le contenu du dossier images, il faut remplacer la variable $themedir par $imagedir lors de l'instaciation de la class FilesystemIterator.

2. Lire tous les fichiers d'un dossier de manière récursive

Lister tous les fichiers contenus dans les sous dossiers se fait à l'aide des classes RecursiveDirectoryIterator et RecursiveIteratorIterator.

<?php
 
$themedir = __DIR__.'/theme';
 
$iterator = new RecursiveDirectoryIterator($themedir, FilesystemIterator::SKIP_DOTS);
 
foreach(new RecursiveIteratorIterator($iterator) as $file) {
    echo $file->getfilename()."\n";
}
 
> php exemple.php
back.css
front.css
logo.png
mainmenu.png
header.jpg
rss_tag.png
 

La classe RecursiveDirectoryIterator étend FilesystemIterator et nécessite l'utilisation de l'iterateur RecursiveIteratorIterator. Sans l'utilisation de cet iterateur, on obtiendrai le même résultat qu'avec la classe FilesystemIterator seule.

3. Lister uniquement les fichiers PNG

On l'a vu au dessus, les classes FilesystemIterator et RecursiveDirectoryIterator listent tous les fichiers sans différence. Il est possible d'ajouter un filtre qui va restreindre le résultat grâce à la classe FilterIterator. Je vais créer une nouvelle classe qui va étendre FilterIterator.

<?php
class pngFileFilterIterator extends FilterIterator
{
    public function accept(): bool
    {
        $sFileInfo = $this->getInnerIterator()->current();
 
        return (preg_match('#\.png$#', $sFileInfo));
    }
}

J'ai surchargé la méthode accept() qui doit retourner un boolean. Sur la première ligne de la méthode j'accède à l'élement courant de l'itérateur interne.
Il faut maintenant prendre en compte cette classe lors du parcours des dossiers.

<?php
 
require 'filter.php';
 
$themedir = __DIR__.'/theme';
 
$iterator = new RecursiveDirectoryIterator($themedir, FilesystemIterator::SKIP_DOTS);
$recursiveIterator = new RecursiveIteratorIterator($iterator);
 
foreach(new pngFileFilterIterator($recursiveIterator) as $file) {
    echo $file->getfilename()."\n";
}
 
> php exemple.php
logo.png
mainmenu.png
rss_tag.png
 

Il suffit de passer l'iterator au constructeur du filtre pour que le résultat soit restreint.

4. Lister uniquement les fichiers de plus de 10ko

C'est le même principe qu'au dessus mais cette fois on va indiquer à la classe RecursiveDirectoryIterator que l'on veut un objet SplFileInfo en sortie.

<?php
class bigFileFilterIterator extends FilterIterator
{
    public function accept(): bool
    {
        $oFileInfo = $this->getInnerIterator()->current();
 
        return ($oFileInfo->getSize() > 10000);
    }
}
 
<?php
 
require 'filter2.php';
 
$themedir = __DIR__.'/theme';
 
$iterator = new RecursiveDirectoryIterator($themedir, FilesystemIterator::SKIP_DOTS);
$iterator->setFlags(FilesystemIterator::CURRENT_AS_FILEINFO);  // <--   nouvelle ligne
 
$recursiveIterator = new RecursiveIteratorIterator($iterator);
 
foreach(new bigFileFilterIterator($recursiveIterator) as $file) {
    echo $file->getfilename()."\n";
}
 
> php exemple.php
back.css
front.css
logo.png
mainmenu.png
 

 

Encore une fois la Simple PHP Library permet de gagner du temps de développement et de rester dans une approche de POO plutôt que de devoir écrire de longs scripts procéduraux avec les fonctions plus classiques de PHP. De plus il est possible d'empiler les filtres; commencer par filtrer les images de type png, puis ajouter un filtre pour n'obtenir que les images de plus de 10ko par exemple.

Il y a 6 commentaires.

Ecrit par montos le 4 janv. 2018

Bjr, article sympa, par contre, pour "filesystemiterator" c'est pas la peine d'ajouter le "skip_dots" puisqu'il prends en charge cette exclusion nativement, ou je me trompe?? cordialement.

Réponse de Ulrich le 4 janv. 2018

En effet \FilesystemIterator ignore les dossiers "." et ".."

Ecrit par guiguiboy le 16 oct. 2012

Bonjour, vous dites qu'il est possible d'empiler les filtres. Pouvez-vous ajouter un exemple de la combinaison des filtres (fichier PNG, taille > 10k) ? Merci.

Réponse de Ulrich le 16 oct. 2012

@guiguiboy: voici le code pour utiliser les 2 filtres.

 
<?php
 
require 'filter.php';
require 'filter2.php';
 
$themedir = __DIR__.'/theme';
 
$iterator = new RecursiveDirectoryIterator($themedir, FilesystemIterator::SKIP_DOTS);
$iterator->setFlags(FilesystemIterator::CURRENT_AS_FILEINFO);
 
$recursiveIterator = new RecursiveIteratorIterator($iterator);
 
$pngIterator = new pngFileFilterIterator($recursiveIterator);
 
foreach(new bigFileFilterIterator($pngIterator) as $file) {
  echo $file->getfilename()."\n";
}
 

 

Ecrit par Grunk le 16 oct. 2012

Salut , y'a un intérêt particulier à utiliser FileSystemIterator plutôt que DirectoryIterator directement ? Je connaissais pas FilesystemItereator et du coup en regardant la doc j'ai pas l'impression que ça propose quoi que ce soit de plus. Sinon pour le filtre d'extension un petit : if(pathinfo($sFileInfo,PATHINFO_EXTENSION) == 'png') c'est toujours plus sympa qu'une regex, surtout si y'a beaucoup de fichier ;)

Réponse de Ulrich le 16 oct. 2012

@Grunk: l'interet que je voies à FilesystemIterator par rapport à DirectoryIterator est la liste des flags que l'on peut setter dans le constructeur comme le FilesystemIterator::SHIP_DOT par exemple. Bien vue pour la fonction pathinfo().

Ajouter un commentaire