Finder : Recherche de fichiers

Vous avez besoin de trouver des fichiers correspondant à un certain masque ? Le Finder peut vous aider. Il s'agit d'un outil polyvalent et rapide pour parcourir la structure des répertoires.

Installation :

composer require nette/utils

Les exemples supposent qu'un alias a été créé :

use Nette\Utils\Finder;

Utilisation de

Tout d'abord, voyons comment vous pouvez utiliser Nette\Utils\Finder pour lister les noms de fichiers avec les extensions .txt et .md dans le répertoire actuel :

foreach (Finder::findFiles(['*.txt', '*.md']) as $name => $file) {
	echo $file;
}

Le répertoire par défaut pour la recherche est le répertoire courant, mais vous pouvez le changer en utilisant les méthodes in() ou from(). La variable $file est une instance de la classe FileInfo avec de nombreuses méthodes utiles. La clé $name contient le chemin d'accès au fichier sous forme de chaîne.

Que rechercher ?

En plus de la méthode findFiles(), il existe également findDirectories(), qui ne recherche que les répertoires, et find(), qui recherche les deux. Ces méthodes sont statiques, elles peuvent donc être appelées sans créer d'instance. Le paramètre mask est facultatif, si vous ne le spécifiez pas, tout est recherché.

foreach (Finder::find() as $file) {
	echo $file; // maintenant tous les fichiers et répertoires sont listés
}

Utilisez les méthodes files() et directories() pour ajouter ce qui doit être recherché. Ces méthodes peuvent être appelées à plusieurs reprises et un tableau de masques peut être fourni en tant que paramètre :

Finder::findDirectories('vendor') // tous les répertoires
	->files(['*.php', '*.phpt']); // plus tous les fichiers PHP

Une alternative aux méthodes statiques est de créer une instance à l'aide de new Finder (l'objet frais créé de cette façon ne cherche rien) et de spécifier ce qu'il faut rechercher à l'aide de files() et directories():

(new Finder)
	->directories()    // tous les répertoires
	->files('*.php');  // plus tous les fichiers PHP

Vous pouvez utiliser des caractères génériques *, **, ? and [...] dans le masque. Vous pouvez même spécifier des répertoires, par exemple src/*.php recherchera tous les fichiers PHP dans le répertoire src.

Les liens symboliques sont également considérés comme des répertoires ou des fichiers.

Le répertoire de recherche par défaut est le répertoire courant. Vous pouvez le modifier en utilisant les méthodes in() et from(). Comme vous pouvez le constater d'après les noms des méthodes, in() ne recherche que dans le répertoire courant, tandis que from() recherche également dans ses sous-répertoires (de manière récursive). Si vous souhaitez effectuer une recherche récursive dans le répertoire courant, vous pouvez utiliser from('.').

Ces méthodes peuvent être appelées plusieurs fois ou vous pouvez leur passer plusieurs chemins sous forme de tableaux, les fichiers seront alors recherchés dans tous les répertoires. Si l'un des répertoires n'existe pas, un message d'erreur ( Nette\UnexpectedValueException ) est lancé.

Finder::findFiles('*.php')
	->in(['src', 'tests']) // recherche directement dans src/ et tests/
	->from('vendor');      // recherche également dans les sous-répertoires vendor/

Les chemins relatifs sont relatifs au répertoire actuel. Bien sûr, des chemins absolus peuvent également être spécifiés :

Finder::findFiles('*.php')
	->in('/var/www/html');

Les caractères génériques *, **, ? can be used in the path. For example, you can use the path src/*/*.php pour rechercher tous les fichiers PHP dans les répertoires de deuxième niveau du répertoire src. Le caractère **, appelé globstar, est un atout puissant car il vous permet de rechercher également dans les sous-répertoires : utilisez src/**/tests/*.php pour rechercher tous les fichiers PHP du répertoire tests situés dans src ou n'importe lequel de ses sous-répertoires.

D'autre part, les caractères de remplacement [...] ne sont pas pris en charge dans le chemin d'accès, c'est-à-dire qu'ils n'ont pas de signification particulière pour éviter tout comportement indésirable dans le cas où vous recherchez par exemple in(__DIR__) et que par hasard les caractères [] apparaissent dans le chemin d'accès.

Lors d'une recherche approfondie de fichiers et de répertoires, le répertoire parent est renvoyé en premier et ensuite les fichiers qu'il contient, ce qui peut être inversé avec childFirst().

Caractères génériques

Vous pouvez utiliser plusieurs caractères spéciaux dans le masque :

  • * – replaces any number of arbitrary characters (except /)
  • ** – remplace un nombre quelconque de caractères arbitraires, y compris / (c'est-à-dire qu'il peut être recherché sur plusieurs niveaux)
  • ? – replaces one arbitrary character (except /)
  • [a-z] – remplace un caractère de la liste de caractères entre crochets.
  • [!a-z] – remplace un caractère en dehors de la liste de caractères entre crochets

Exemples d'utilisation :

  • img/?.png – fichiers avec le nom à une lettre 0.png, 1.png, x.png, etc.
  • logs/[0-9][0-9][0-9][0-9]-[01][0-9]-[0-3][0-9].log – fichiers journaux au format YYYY-MM-DD
  • src/**/tests/* – fichiers dans le répertoire src/tests, src/foo/tests, src/foo/bar/tests et ainsi de suite.
  • docs/**.md – tous les fichiers avec l'extension .md dans tous les sous-répertoires du répertoire docs

A l'exception de

Utilisez la méthode exclude() pour exclure des fichiers et des répertoires des recherches. Vous spécifiez un masque auquel le fichier ne doit pas correspondre. Exemple de recherche de fichiers *.txt à l'exception de ceux contenant la lettre X dans le nom :

Finder::findFiles('*.txt')
	->exclude('*X*');

Utilisez exclude() pour ignorer les sous-répertoires parcourus :

Finder::findFiles('*.php')
	->from($dir)
	->exclude('temp', '.git')

Filtrage de

Le Finder propose plusieurs méthodes pour filtrer les résultats (c'est-à-dire les réduire). Vous pouvez les combiner et les appeler à plusieurs reprises.

Utilisez size() pour filtrer par taille de fichier. De cette façon, nous trouvons les fichiers dont la taille est comprise entre 100 et 200 octets :

Finder::findFiles('*.php')
	->size('>=', 100)
	->size('<=', 200);

La méthode date() permet de filtrer par la date de la dernière modification du fichier. Les valeurs peuvent être absolues ou relatives à la date et à l'heure actuelles. Par exemple, voici comment trouver les fichiers modifiés au cours des deux dernières semaines :

Finder::findFiles('*.php')
	->date('>', '-2 weeks')
	->from($dir)

Les deux fonctions comprennent les opérateurs >, >=, <, <=, =, !=, <>.

Le Finder vous permet également de filtrer les résultats à l'aide de fonctions personnalisées. La fonction reçoit un objet Nette\Utils\FileInfo comme paramètre et doit retourner true pour inclure le fichier dans les résultats.

Exemple : rechercher les fichiers PHP qui contiennent la chaîne de caractères Nette (insensible à la casse) :

Finder::findFiles('*.php')
	->filter(fn($file) => strcasecmp($file->read(), 'Nette') === 0);

Filtrage en profondeur

Lors d'une recherche récursive, vous pouvez définir la profondeur d'exploration maximale à l'aide de la méthode limitDepth(). Si vous définissez limitDepth(1), seuls les premiers sous-répertoires sont explorés, limitDepth(0) désactive l'exploration en profondeur, et une valeur de –1 annule la limite.

Le Finder vous permet d'utiliser ses propres fonctions pour décider du répertoire à pénétrer lors de l'exploration. La fonction reçoit un objet Nette\Utils\FileInfo comme paramètre et doit retourner true pour entrer dans le répertoire :

Finder::findFiles('*.php')
	->descentFilter($file->getBasename() !== 'temp');

Triage

Le Finder offre également plusieurs fonctions permettant de trier les résultats.

La méthode sortByName() trie les résultats par nom de fichier. Le tri est naturel, c'est-à-dire qu'il traite correctement les chiffres dans les noms et renvoie par exemple foo1.txt avant foo10.txt.

Le Finder vous permet également de trier en utilisant une fonction personnalisée. Elle prend deux objets Nette\Utils\FileInfo comme paramètres et doit retourner le résultat de la comparaison avec l'opérateur <=>c'est-à-dire -1, 0 nebo 1. Par exemple, voici comment trier les fichiers par taille :

$finder->sortBy(fn($a, $b) => $a->getSize() <=> $b->getSize());

Plusieurs recherches différentes

Si vous devez trouver plusieurs fichiers différents dans des endroits différents ou répondant à des critères différents, utilisez la méthode append(). Elle renvoie un nouvel objet Finder afin que vous puissiez enchaîner les appels de méthode :

($finder = new Finder) // enregistre le premier Finder dans la variable $finder !
	->files('*.php')   // recherche les fichiers *.php dans src/
	->from('src')
	->append()
	->files('*.md')    // dans docs/ recherche les fichiers *.md
	->from('docs')
	->append()
	->files('*.json'); // dans le dossier actuel, recherche des fichiers *.json

Alternativement, vous pouvez utiliser la méthode append() pour ajouter un fichier spécifique (ou un tableau de fichiers). Elle renvoie alors le même objet Finder:

$finder = Finder::findFiles('*.txt')
	->append(__FILE__);

FileInfo

Nette\Utils\FileInfo est une classe représentant un fichier ou un répertoire dans les résultats de recherche. Il s'agit d'une extension de la classe SplFileInfo qui fournit des informations telles que la taille du fichier, la date de dernière modification, le nom, le chemin, etc.

De plus, elle fournit des méthodes pour renvoyer les chemins relatifs, ce qui est utile lors d'une navigation en profondeur :

foreach (Finder::findFiles('*.jpg')->from('.') as $file) {
	$absoluteFilePath = $file->getRealPath();
	$relativeFilePath = $file->getRelativePathname();
}

Vous disposez également de méthodes pour lire et écrire le contenu d'un fichier :

foreach ($finder as $file) {
    $contents = $file->read();
    // ...
    $file->write($contents);
}

Renvoyer les résultats sous forme de tableau

Comme nous l'avons vu dans les exemples, le Finder implémente l'interface IteratorAggregate, ce qui vous permet d'utiliser foreach pour parcourir les résultats. Il est programmé de manière à ce que les résultats ne soient chargés qu'au fur et à mesure de la navigation, donc si vous avez un grand nombre de fichiers, il n'attend pas qu'ils soient tous lus.

Vous pouvez également obtenir les résultats sous la forme d'un tableau d'objets Nette\Utils\FileInfo, en utilisant la méthode collect(). Le tableau n'est pas associatif, mais numérique.

$array = $finder->findFiles('*.php')->collect();
version: 4.0