Finder: Dateisuche

Müssen Sie Dateien finden, die einer bestimmten Maske entsprechen? Der Finder kann Ihnen helfen. Er ist ein vielseitiges und schnelles Werkzeug zum Durchsuchen der Verzeichnisstruktur.

Installation:

composer require nette/utils

Die Beispiele gehen davon aus, dass ein Alias erstellt wurde:

use Nette\Utils\Finder;

Verwendung von

Sehen wir uns zunächst an, wie Sie Nette\Utils\Finder verwenden können, um die Dateinamen mit den Erweiterungen .txt und .md im aktuellen Verzeichnis aufzulisten:

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.

Wonach soll gesucht werden?

Neben der Methode findFiles() gibt es auch findDirectories(), die nur Verzeichnisse durchsucht, und find(), die beide durchsucht. Diese Methoden sind statisch, d.h. sie können aufgerufen werden, ohne eine Instanz zu erzeugen. Der Parameter mask ist optional, wenn Sie ihn nicht angeben, wird alles durchsucht.

foreach (Finder::find() as $file) {
	echo $file; // jetzt sind alle Dateien und Verzeichnisse aufgelistet
}

Verwenden Sie die Methoden files() und directories(), um hinzuzufügen, wonach sonst noch gesucht werden soll. Die Methoden können wiederholt aufgerufen werden und ein Array von Masken kann als Parameter übergeben werden:

Finder::findDirectories('vendor') // alle Verzeichnisse
	->files(['*.php', '*.phpt']); // sowie alle PHP-Dateien

Eine Alternative zu statischen Methoden besteht darin, mit new Finder eine Instanz zu erstellen (das auf diese Weise neu erstellte Objekt sucht nach nichts) und mit files() und directories() anzugeben, wonach gesucht werden soll:

(new Finder)
	->directories()   // alle Verzeichnisse
	->files('*.php'); // sowie alle PHP-Dateien

Sie können Wildcards *, ** verwenden, ? and [...] in der Maske verwenden. Sie können sogar Verzeichnisse angeben, z. B. src/*.php sucht nach allen PHP-Dateien im Verzeichnis src.

Symlinks werden auch als Verzeichnisse oder Dateien betrachtet.

Das Standard-Suchverzeichnis ist das aktuelle Verzeichnis. Sie können dies ändern, indem Sie die Methoden in() und from() verwenden. Wie Sie aus den Namen der Methoden ersehen können, durchsucht in() nur das aktuelle Verzeichnis, während from() auch die Unterverzeichnisse durchsucht (rekursiv). Wenn Sie rekursiv im aktuellen Verzeichnis suchen wollen, können Sie from('.') verwenden.

Diese Methoden können mehrfach aufgerufen werden oder Sie können ihnen mehrere Pfade als Arrays übergeben, dann werden Dateien in allen Verzeichnissen gesucht. Wenn eines der Verzeichnisse nicht existiert, wird ein Nette\UnexpectedValueException geworfen.

Finder::findFiles('*.php')
	->in(['src', 'tests']) // sucht direkt in src/ und tests/
	->from('vendor');      // sucht auch in den 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');

Mit den Platzhaltern *, **, ? can be used in the path. For example, you can use the path src/*/*.php können Sie nach allen PHP-Dateien in den Verzeichnissen der zweiten Ebene im Verzeichnis src suchen. Das Zeichen **, globstar genannt, ist ein mächtiger Trumpf, weil es Ihnen erlaubt, auch Unterverzeichnisse zu durchsuchen: Verwenden Sie src/**/tests/*.php, um nach allen PHP-Dateien im Verzeichnis tests zu suchen, das sich in src oder einem seiner Unterverzeichnisse befindet.

Andererseits werden Wildcards [...] Zeichen werden im Pfad nicht unterstützt, d.h. sie haben keine besondere Bedeutung, um unerwünschtes Verhalten zu vermeiden, falls Sie z.B. nach in(__DIR__) suchen und zufällig [] Zeichen im Pfad erscheinen.

Bei der Suche nach Dateien und Verzeichnissen in der Tiefe wird zuerst das übergeordnete Verzeichnis zurückgegeben und dann die darin enthaltenen Dateien, was sich mit childFirst() umkehren lässt.

Wildcards

Sie können mehrere Sonderzeichen in der Maske verwenden:

  • * – replaces any number of arbitrary characters (except /)
  • ** – ersetzt eine beliebige Anzahl von Zeichen einschließlich / (d.h. es kann mehrstufig gesucht werden)
  • ? – replaces one arbitrary character (except /)
  • [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

Beispiele für die Verwendung:

  • img/?.png – Dateien mit dem Einbuchstabennamen 0.png, 1.png, x.png, usw.
  • logs/[0-9][0-9][0-9][0-9]-[01][0-9]-[0-3][0-9].log – Protokolldateien im Format YYYY-MM-DD
  • src/**/tests/* – Dateien im Verzeichnis src/tests, src/foo/tests, src/foo/bar/tests und so weiter.
  • docs/**.md – alle Dateien mit der Erweiterung .md in allen Unterverzeichnissen des Verzeichnisses docs

Ausgenommen

Verwenden Sie die Methode exclude(), um Dateien und Verzeichnisse von der Suche auszuschließen. Sie geben eine Maske an, der die Datei nicht entsprechen darf. Beispiel für die Suche nach Dateien *.txt mit Ausnahme derjenigen, die den Buchstaben “X” im Namen enthalten:

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

Verwenden Sie exclude(), um durchsuchte Unterverzeichnisse zu überspringen:

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

Filtern von

Der Finder bietet mehrere Methoden, um die Ergebnisse zu filtern (d.h. zu reduzieren). Sie können sie kombinieren und wiederholt aufrufen.

Verwenden Sie size(), um nach der Dateigröße zu filtern. Auf diese Weise finden wir Dateien mit Größen zwischen 100 und 200 Byte:

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

Die Methode date() filtert nach dem Datum, an dem die Datei zuletzt geändert wurde. Die Werte können absolut oder relativ zum aktuellen Datum und zur aktuellen Uhrzeit angegeben werden, um z. B. Dateien zu finden, die in den letzten zwei Wochen geändert wurden:

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

Beide Funktionen verstehen die Operatoren >, >=, <, <=, =, !=, <>.

Der Finder erlaubt Ihnen auch, die Ergebnisse mit Hilfe von benutzerdefinierten Funktionen zu filtern. Die Funktion erhält ein Nette\Utils\FileInfo Objekt als Parameter und muss true zurückgeben, um die Datei in die Ergebnisse aufzunehmen.

Beispiel: Suche nach PHP-Dateien, die die Zeichenkette Nette enthalten (Groß- und Kleinschreibung wird nicht berücksichtigt):

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

Tiefenfilterung

Bei der rekursiven Suche können Sie mit der Methode limitDepth() die maximale Crawl-Tiefe festlegen. Wenn Sie limitDepth(1) einstellen, werden nur die ersten Unterverzeichnisse gecrawlt, limitDepth(0) deaktiviert das Crawlen in der Tiefe und ein Wert von –1 hebt die Begrenzung auf.

Der Finder erlaubt Ihnen, seine eigenen Funktionen zu verwenden, um zu entscheiden, welches Verzeichnis beim Durchsuchen eingegeben werden soll. Die Funktion erhält ein Nette\Utils\FileInfo Objekt als Parameter und muss true zurückgeben, um das Verzeichnis zu betreten:

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

Sortieren

Der Finder bietet auch mehrere Funktionen zum Sortieren von Ergebnissen.

Die Methode sortByName() sortiert die Ergebnisse nach Dateinamen. Die Sortierung ist natürlich, d.h. sie behandelt die Zahlen in den Namen korrekt und gibt z.B. foo1.txt vor foo10.txt zurück.

Der Finder erlaubt Ihnen auch, mit einer eigenen Funktion zu sortieren. Sie nimmt zwei Nette\Utils\FileInfo Objekte als Parameter und muss das Ergebnis des Vergleichs mit dem Operator <=>zurückgeben, d.h. -1, 0 nebo 1. So sortieren wir zum Beispiel Dateien nach Größe:

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

Mehrere verschiedene Suchvorgänge

Wenn Sie mehrere verschiedene Dateien an unterschiedlichen Orten oder nach unterschiedlichen Kriterien suchen müssen, verwenden Sie die Methode append(). Sie gibt ein neues Finder Objekt zurück, so dass Sie Methodenaufrufe verketten können:

($finder = new Finder) // speichert den ersten Finder in der Variable $finder!
	->files('*.php')   // Suche nach *.php Dateien in src/
	->from('src')
	->append()
	->files('*.md')    // in docs/ suchen Sie nach *.md-Dateien
	->from('docs')
	->append()
	->files('*.json'); // im aktuellen Ordner wird nach *.json-Dateien gesucht

Alternativ können Sie auch die Methode append() verwenden, um eine bestimmte Datei (oder ein Array von Dateien) hinzuzufügen. Sie gibt dann das gleiche Objekt Finder 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. Sie ist eine Erweiterung der Klasse SplFileInfo, die Informationen wie Dateigröße, Datum der letzten Änderung, Name, Pfad usw. liefert.

Darüber hinaus bietet sie Methoden zur Rückgabe relativer Pfade, was bei der Suche in der Tiefe nützlich ist:

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

Sie haben auch Methoden zum Lesen und Schreiben des Inhalts einer Datei:

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

Ergebnisse als Array zurückgeben

Wie in den Beispielen gesehen, implementiert der Finder die Schnittstelle IteratorAggregate, so dass Sie foreach verwenden können, um die Ergebnisse zu durchsuchen. Er ist so programmiert, dass die Ergebnisse erst beim Durchsuchen geladen werden. Wenn Sie also eine große Anzahl von Dateien haben, wartet er nicht darauf, dass alle gelesen werden.

Sie können sich die Ergebnisse auch als Array von Nette\Utils\FileInfo -Objekten zurückgeben lassen, indem Sie die Methode collect() verwenden. Das Array ist nicht assoziativ, sondern numerisch.

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