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 der folgende Alias definiert 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. Der Schlüssel $name
enthält den 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 und können daher ohne Erstellung einer Instanz aufgerufen
werden. 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 leeres
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 behandelt.
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 angegebenen Verzeichnissen gesucht. Wenn eines der Verzeichnisse nicht existiert, wird eine
Nette\UnexpectedValueException
ausgelöst.
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.
Bei der Tiefensuche von Dateien und Verzeichnissen wird standardmäßig zuerst das übergeordnete Verzeichnis und dann dessen
Inhalt zurückgegeben. Dieses Verhalten kann mit childFirst()
umgekehrt werden.
Platzhalter
In der Maske können Sie folgende 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 in den Verzeichnissensrc/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 bestimmter Unterverzeichnisse zu überspringen:
Finder::findFiles('*.php')
->from($dir)
->exclude('temp', '.git')
Filterung
Der Finder bietet mehrere Methoden zum Filtern (d.h. Reduzieren) der Ergebnisse. 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 Methoden unterstützen die Operatoren >
, >=
, <
, <=
,
=
, !=
, <>
.
Der Finder ermöglicht auch das Filtern der Ergebnisse mithilfe benutzerdefinierter Funktionen. Die Funktion erhält als
Parameter ein Nette\Utils\FileInfo
-Objekt 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 Suchtiefe 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
(Standard) hebt das Limit auf.
Der Finder ermöglicht es, mithilfe benutzerdefinierter Funktionen zu entscheiden, welche Verzeichnisse während der Suche
betreten werden sollen. Die Funktion erhält als Parameter ein Nette\Utils\FileInfo
-Objekt, das das Verzeichnis
repräsentiert, und muss true
zurückgeben, damit in das Verzeichnis eingetreten wird:
Finder::findFiles('*.php')
->descentFilter(fn($file) => $file->getBasename() !== 'temp');
Sortierung
Der Finder bietet auch mehrere Funktionen zum Sortieren der Ergebnisse.
Die Methode sortByName()
sortiert die Ergebnisse nach Dateinamen. Die Sortierung erfolgt natürlich, d.h. sie
behandelt Zahlen in Namen korrekt und gibt z.B. foo1.txt
vor foo10.txt
zurück.
Der Finder ermöglicht auch die Sortierung mithilfe einer benutzerdefinierten 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 unterschiedliche Dateien an verschiedenen Orten oder mit unterschiedlichen Kriterien finden müssen, 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 Variable $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. In diesem Fall 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 repräsentiert. Sie erweitert die Klasse SplFileInfo und stellt Informationen wie Dateigröße, letztes Änderungsdatum, Name, Pfad usw. bereit.
Zusätzlich bietet sie Methoden zur Rückgabe des relativen Pfads, was bei der Tiefensuche nützlich ist:
foreach (Finder::findFiles('*.jpg')->from('.') as $file) {
$absoluteFilePath = $file->getRealPath();
$relativeFilePath = $file->getRelativePathname();
}
Außerdem stehen 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 IteratorAggregate
-Interface, sodass Sie
foreach
verwenden können, um die Ergebnisse zu durchlaufen. Er ist so implementiert, dass die Ergebnisse erst
während des Durchlaufens geladen werden (Lazy Loading). Wenn Sie also eine große Anzahl von Dateien haben, müssen Sie nicht
warten, bis alle eingelesen sind.
Mit der Methode collect()
können Sie die Ergebnisse auch als Array von Nette\Utils\FileInfo
-Objekten
erhalten. Das Array ist numerisch indiziert (nicht assoziativ).
$array = $finder->findFiles('*.php')->collect();