Finder: Dateisuche
Müssen Sie Dateien finden, die einer bestimmten Maske entsprechen? Der Finder hilft Ihnen dabei. Es ist ein vielseitiges und schnelles Werkzeug zum Durchsuchen der Verzeichnisstruktur.
Installation:
composer require nette/utils
Die Beispiele setzen voraus, dass ein Alias erstellt wurde:
use Nette\Utils\Finder;
Verwendung
Zuerst zeigen wir, wie Sie mit Nette\Utils\Finder
Dateinamen mit den Erweiterungen .txt
und .md
im aktuellen Verzeichnis auflisten können:
foreach (Finder::findFiles(['*.txt', '*.md']) as $name => $file) {
echo $file;
}
Das Standardverzeichnis für die Suche ist das aktuelle Verzeichnis, aber Sie können es mit den Methoden in() oder from() ändern. Die Variable $file
ist eine Instanz der Klasse FileInfo mit vielen nützlichen Methoden. Im Schlüssel $name
befindet sich der Pfad zur
Datei als String.
Was soll gesucht werden?
Neben der Methode findFiles()
gibt es auch findDirectories()
, die nur Verzeichnisse sucht, und
find()
, die beides sucht. Diese Methoden sind statisch, sodass sie ohne Erstellung einer Instanz aufgerufen werden
können. Der Parameter mit der Maske ist optional, wenn Sie ihn nicht angeben, wird alles gesucht.
foreach (Finder::find() as $file) {
echo $file; // jetzt werden alle Dateien und Verzeichnisse ausgegeben
}
Mit den Methoden files()
und directories()
können Sie ergänzen, was zusätzlich gesucht werden
soll. Die Methoden können wiederholt aufgerufen werden, und als Parameter kann auch ein Array von Masken angegeben werden:
Finder::findDirectories('vendor') // alle Verzeichnisse
->files(['*.php', '*.phpt']); // plus alle PHP-Dateien
Eine Alternative zu statischen Methoden ist die Erstellung einer Instanz mit new Finder
(ein so erstelltes
frisches Objekt sucht nichts) und die Angabe, was gesucht werden soll, mit files()
und
directories()
:
(new Finder)
->directories() // alle Verzeichnisse
->files('*.php'); // plus alle PHP-Dateien
In der Maske können Sie die Platzhalter *
, **
, ?
und
[...]
verwenden. Sie können sogar Verzeichnisse angeben, zum Beispiel sucht src/*.php
alle PHP-Dateien
im Verzeichnis src
.
Symlinks werden ebenfalls als Verzeichnisse oder Dateien betrachtet.
Wo soll gesucht werden?
Das Standardverzeichnis für die Suche ist das aktuelle Verzeichnis. Sie ändern es mit den Methoden in()
und
from()
. Wie aus den Methodennamen ersichtlich ist, sucht in()
nur im angegebenen Verzeichnis, während
from()
auch in dessen Unterverzeichnissen (rekursiv) sucht. Wenn Sie rekursiv im aktuellen Verzeichnis suchen
möchten, können Sie from('.')
verwenden.
Diese Methoden können mehrmals aufgerufen werden oder ihnen mehrere Pfade als Array übergeben werden, die Dateien werden dann
in allen Verzeichnissen gesucht. Wenn eines der Verzeichnisse nicht existiert, wird eine Ausnahme
Nette\UnexpectedValueException
geworfen.
Finder::findFiles('*.php')
->in(['src', 'tests']) // sucht direkt in src/ und tests/
->from('vendor'); // sucht auch in Unterverzeichnissen von vendor/
Relative Pfade sind relativ zum aktuellen Verzeichnis. Es können natürlich auch absolute Pfade angegeben werden:
Finder::findFiles('*.php')
->in('/var/www/html');
Im Pfad können die Platzhalter *
, **
, ?
verwendet
werden. Sie können beispielsweise mit dem Pfad src/*/*.php
alle PHP-Dateien in Verzeichnissen der zweiten Ebene im
Verzeichnis src
suchen. Das Zeichen **
, genannt Globstar, ist ein mächtiger Trumpf, da es die Suche
auch in Unterverzeichnissen ermöglicht: Mit src/**/tests/*.php
suchen Sie alle PHP-Dateien im Verzeichnis
tests
, das sich in src
oder einem seiner Unterverzeichnisse befindet.
Im Gegensatz dazu werden die Platzhalter [...]
im Pfad nicht unterstützt, d.h. sie haben keine besondere
Bedeutung, um unerwünschtes Verhalten zu vermeiden, falls Sie beispielsweise in(__DIR__)
suchen und zufällig im
Pfad die Zeichen []
vorkommen.
Beim Suchen von Dateien und Verzeichnissen in der Tiefe wird zuerst das übergeordnete Verzeichnis zurückgegeben und erst
danach die darin enthaltenen Dateien, was mit childFirst()
umgekehrt werden kann.
Platzhalter
In der Maske können Sie mehrere Sonderzeichen verwenden:
*
– ersetzt eine beliebige Anzahl beliebiger Zeichen (außer/
)**
– ersetzt eine beliebige Anzahl beliebiger Zeichen einschließlich/
(d.h. es kann mehrstufig gesucht werden)?
– ersetzt ein beliebiges Zeichen (außer/
)[a-z]
– ersetzt ein Zeichen aus der Liste der Zeichen in eckigen Klammern[!a-z]
– ersetzt ein Zeichen außerhalb der Liste der Zeichen in eckigen Klammern
Anwendungsbeispiele:
img/?.png
– Dateien mit einbuchstabigem Namen0.png
,1.png
,x.png
, usw.logs/[0-9][0-9][0-9][0-9]-[01][0-9]-[0-3][0-9].log
– Logs im FormatYYYY-MM-DD
src/**/tests/*
– Dateien im Verzeichnissrc/tests
,src/foo/tests
,src/foo/bar/tests
und so weiter.docs/**.md
– alle Dateien mit der Erweiterung.md
in allen Unterverzeichnissen des Verzeichnissesdocs
Ausschluss
Mit der Methode exclude()
können Dateien und Verzeichnisse von der Suche ausgeschlossen werden. Sie geben eine
Maske an, der die Datei nicht entsprechen darf. Beispiel für die Suche nach *.txt
-Dateien außer denen, die den
Buchstaben X
im Namen enthalten:
Finder::findFiles('*.txt')
->exclude('*X*');
Verwenden Sie exclude()
, um das Durchsuchen von Unterverzeichnissen zu überspringen:
Finder::findFiles('*.php')
->from($dir)
->exclude('temp', '.git')
Filterung
Der Finder bietet mehrere Methoden zum Filtern der Ergebnisse (d.h. zu ihrer Reduzierung). Sie können sie kombinieren und wiederholt aufrufen.
Mit size()
filtern wir nach Dateigröße. So finden wir Dateien mit einer Größe zwischen 100 und
200 Bytes:
Finder::findFiles('*.php')
->size('>=', 100)
->size('<=', 200);
Die Methode date()
filtert nach dem Datum der letzten Änderung der Datei. Die Werte können absolut oder relativ
zum aktuellen Datum und zur aktuellen Uhrzeit sein, zum Beispiel finden wir so Dateien, die in den letzten zwei Wochen geändert
wurden:
Finder::findFiles('*.php')
->date('>', '-2 weeks')
->from($dir)
Beide Funktionen verstehen die Operatoren >
, >=
, <
, <=
,
=
, !=
, <>
.
Der Finder ermöglicht auch das Filtern der Ergebnisse mithilfe eigener Funktionen. Die Funktion erhält als Parameter das
Objekt Nette\Utils\FileInfo
und muss true
zurückgeben, damit die Datei in die Ergebnisse
aufgenommen wird.
Beispiel: Suche nach PHP-Dateien, die den String Nette
enthalten (unabhängig von der Groß-/Kleinschreibung):
Finder::findFiles('*.php')
->filter(fn($file) => strcasecmp($file->read(), 'Nette') === 0);
Tiefenfilterung
Bei der rekursiven Suche können Sie die maximale Tiefe des Durchlaufs mit der Methode limitDepth()
festlegen.
Wenn Sie limitDepth(1)
setzen, werden nur die ersten Unterverzeichnisse durchlaufen, limitDepth(0)
deaktiviert den Tiefendurchlauf und der Wert –1 hebt das Limit auf.
Der Finder ermöglicht es, mithilfe eigener Funktionen zu entscheiden, in welches Verzeichnis beim Durchlaufen eingetreten
werden soll. Die Funktion erhält als Parameter das Objekt Nette\Utils\FileInfo
und muss true
zurückgeben, damit in das Verzeichnis eingetreten wird:
Finder::findFiles('*.php')
->descentFilter($file->getBasename() !== 'temp');
Sortierung
Der Finder bietet auch mehrere Funktionen zum Sortieren der Ergebnisse.
Die Methode sortByName()
sortiert die Ergebnisse nach Dateinamen. Die Sortierung ist natürlich, d.h. sie kommt
korrekt mit Zahlen in Namen zurecht und gibt z.B. foo1.txt
vor foo10.txt
zurück.
Der Finder ermöglicht auch die Sortierung mithilfe einer eigenen Funktion. Diese erhält als Parameter zwei
Nette\Utils\FileInfo
-Objekte und muss das Ergebnis des Vergleichs mit dem Operator <=>
zurückgeben, also -1
, 0
oder 1
. Zum Beispiel sortieren wir so Dateien nach Größe:
$finder->sortBy(fn($a, $b) => $a->getSize() <=> $b->getSize());
Mehrere verschiedene Suchen
Wenn Sie mehrere verschiedene Dateien an verschiedenen Orten finden müssen oder solche, die andere Kriterien erfüllen,
verwenden Sie die Methode append()
. Sie gibt ein neues Finder
-Objekt zurück, sodass Methodenaufrufe
verkettet werden können:
($finder = new Finder) // wir speichern den ersten Finder in der Variablen $finder!
->files('*.php') // in src/ suchen wir nach *.php Dateien
->from('src')
->append()
->files('*.md') // in docs/ suchen wir nach *.md Dateien
->from('docs')
->append()
->files('*.json'); // im aktuellen Ordner suchen wir nach *.json Dateien
Alternativ kann die Methode append()
verwendet werden, um eine bestimmte Datei (oder ein Array von Dateien)
hinzuzufügen. Dann gibt sie dasselbe Finder
-Objekt zurück:
$finder = Finder::findFiles('*.txt')
->append(__FILE__);
FileInfo
Nette\Utils\FileInfo ist eine Klasse, die eine Datei oder ein Verzeichnis in den Suchergebnissen darstellt. Es handelt sich um eine Erweiterung der Klasse SplFileInfo, die Informationen wie Dateigröße, letztes Änderungsdatum, Name, Pfad usw. bereitstellt.
Zusätzlich bietet sie Methoden zum Zurückgeben des relativen Pfads, was beim Tiefendurchlauf nützlich ist:
foreach (Finder::findFiles('*.jpg')->from('.') as $file) {
$absoluteFilePath = $file->getRealPath();
$relativeFilePath = $file->getRelativePathname();
}
Weiterhin stehen Ihnen Methoden zum Lesen und Schreiben des Dateiinhalts zur Verfügung:
foreach ($finder as $file) {
$contents = $file->read();
// ...
$file->write($contents);
}
Rückgabe der Ergebnisse als Array
Wie in den Beispielen gezeigt, implementiert der Finder das Interface IteratorAggregate
, sodass Sie
foreach
verwenden können, um die Ergebnisse zu durchlaufen. Er ist so programmiert, dass die Ergebnisse nur während
des Durchlaufens geladen werden. Wenn Sie also eine große Anzahl von Dateien haben, wird nicht gewartet, bis alle gelesen
wurden.
Sie können sich die Ergebnisse auch als Array von Nette\Utils\FileInfo
-Objekten zurückgeben lassen, und zwar mit
der Methode collect()
. Das Array ist nicht assoziativ, sondern numerisch.
$array = $finder->findFiles('*.php')->collect();