Finder: wyszukiwanie plików
Potrzebujesz znaleźć pliki pasujące do określonej maski? Finder Ci w tym pomoże. Jest to wszechstronne i szybkie narzędzie do przeglądania struktury katalogów.
Instalacja:
composer require nette/utils
Przykłady zakładają utworzony alias:
use Nette\Utils\Finder;
Użycie
Najpierw pokażemy, jak za pomocą Nette\Utils\Finder
można wypisać nazwy plików z rozszerzeniami .txt
i .md
w bieżącym katalogu:
foreach (Finder::findFiles(['*.txt', '*.md']) as $name => $file) {
echo $file;
}
Domyślnym katalogiem do wyszukiwania jest bieżący katalog, ale można go zmienić za pomocą metod in() lub from(). Zmienna $file
jest instancją klasy FileInfo z wieloma przydatnymi metodami. W kluczu $name
znajduje się ścieżka do pliku
jako ciąg znaków.
Czego szukać?
Oprócz metody findFiles()
istnieje również findDirectories()
, która szuka tylko katalogów, oraz
find()
, która szuka obu. Te metody są statyczne, więc można je wywoływać bez tworzenia instancji. Parametr
z maską jest opcjonalny, jeśli go nie podasz, wyszukane zostanie wszystko.
foreach (Finder::find() as $file) {
echo $file; // teraz zostaną wypisane wszystkie pliki i katalogi
}
Za pomocą metod files()
i directories()
możesz uzupełniać, co jeszcze ma być wyszukiwane. Metody
można wywoływać wielokrotnie, a jako parametr można podać również tablicę masek:
Finder::findDirectories('vendor') // wszystkie katalogi
->files(['*.php', '*.phpt']); // plus wszystkie pliki PHP
Alternatywą dla metod statycznych jest utworzenie instancji za pomocą new Finder
(tak utworzony świeży obiekt
niczego nie wyszukuje) i określenie, czego szukać za pomocą files()
i directories()
:
(new Finder)
->directories() // wszystkie katalogi
->files('*.php'); // plus wszystkie pliki PHP
W masce można używać symboli wieloznacznych *
, **
,
?
i [...]
. Można nawet określić katalogi, na przykład src/*.php
wyszuka wszystkie pliki
PHP w katalogu src
.
Linki symboliczne są również traktowane jako katalogi lub pliki.
Gdzie szukać?
Domyślnym katalogiem do wyszukiwania jest bieżący katalog. Zmienisz go za pomocą metod in()
i
from()
. Jak wynika z nazw metod, in()
szuka tylko w danym katalogu, podczas gdy from()
szuka również w jego podkatalogach (rekurencyjnie). Jeśli chcesz wyszukiwać rekurencyjnie w bieżącym katalogu, możesz
użyć from('.')
.
Te metody można wywoływać wielokrotnie lub przekazać im wiele ścieżek jako tablicę, pliki będą wtedy szukane we
wszystkich katalogach. Jeśli któryś z katalogów nie istnieje, zostanie rzucony wyjątek
Nette\UnexpectedValueException
.
Finder::findFiles('*.php')
->in(['src', 'tests']) // szuka bezpośrednio w src/ i tests/
->from('vendor'); // szuka również w podkatalogach vendor/
Ścieżki względne są względne względem bieżącego katalogu. Oczywiście można również podać ścieżki absolutne:
Finder::findFiles('*.php')
->in('/var/www/html');
W ścieżce można używać symboli wieloznacznych symbole wieloznaczne *
,
**
, ?
. Możesz na przykład za pomocą ścieżki src/*/*.php
szukać wszystkich plików PHP
w katalogach drugiego poziomu w katalogu src
. Znak **
nazywany globstar jest potężnym atutem,
ponieważ umożliwia szukanie również w podkatalogach: za pomocą src/**/tests/*.php
szukasz wszystkich plików PHP
w katalogu tests
znajdującym się w src
lub dowolnym jego podkatalogu.
Natomiast symbole wieloznaczne [...]
w ścieżce nie są obsługiwane, tj. nie mają specjalnego znaczenia, aby
nie dochodziło do niepożądanego zachowania w przypadku, gdy będziesz szukać na przykład in(__DIR__)
i przypadkiem w ścieżce pojawią się znaki []
.
Podczas wyszukiwania plików i katalogów w głąb, najpierw zwracany jest katalog nadrzędny, a dopiero potem pliki w nim
zawarte, co można odwrócić za pomocą childFirst()
.
Symbole wieloznaczne
W masce można używać kilku specjalnych znaków:
*
– zastępuje dowolną liczbę dowolnych znaków (oprócz/
)**
– zastępuje dowolną liczbę dowolnych znaków, w tym/
(tj. można szukać wielopoziomowo)?
– zastępuje jeden dowolny znak (oprócz/
)[a-z]
– zastępuje jeden znak z listy znaków w nawiasach kwadratowych[!a-z]
– zastępuje jeden znak spoza listy znaków w nawiasach kwadratowych
Przykłady użycia:
img/?.png
– pliki o jednoliterowej nazwie0.png
,1.png
,x.png
, itp.logs/[0-9][0-9][0-9][0-9]-[01][0-9]-[0-3][0-9].log
– logi w formacieYYYY-MM-DD
src/**/tests/*
– pliki w katalogusrc/tests
,src/foo/tests
,src/foo/bar/tests
i tak dalej.docs/**.md
– wszystkie pliki z rozszerzeniem.md
we wszystkich podkatalogach katalogudocs
Wykluczenie
Za pomocą metody exclude()
można wykluczyć pliki i katalogi z wyszukiwania. Podajesz maskę, której plik nie
może pasować. Przykład wyszukiwania plików *.txt
oprócz tych, które zawierają w nazwie literę
X
:
Finder::findFiles('*.txt')
->exclude('*X*');
Aby pominąć przeglądane podkatalogi, użyj exclude()
:
Finder::findFiles('*.php')
->from($dir)
->exclude('temp', '.git')
Filtrowanie
Finder oferuje kilka metod filtrowania wyników (tj. ich redukcji). Możesz je łączyć i wywoływać wielokrotnie.
Za pomocą size()
filtrujemy według rozmiaru pliku. W ten sposób znajdziemy pliki o rozmiarze w zakresie od
100 do 200 bajtów:
Finder::findFiles('*.php')
->size('>=', 100)
->size('<=', 200);
Metoda date()
filtruje według daty ostatniej modyfikacji pliku. Wartości mogą być absolutne lub względne
względem bieżącej daty i czasu, na przykład w ten sposób znajdziemy pliki zmodyfikowane w ciągu ostatnich dwóch
tygodni:
Finder::findFiles('*.php')
->date('>', '-2 weeks')
->from($dir)
Obie funkcje rozumieją operatory >
, >=
, <
, <=
, =
,
!=
, <>
.
Finder umożliwia również filtrowanie wyników za pomocą własnych funkcji. Funkcja otrzymuje jako parametr obiekt
Nette\Utils\FileInfo
i musi zwrócić true
, aby plik został uwzględniony w wynikach.
Przykład: wyszukiwanie plików PHP, które zawierają ciąg znaków Nette
(bez względu na wielkość liter):
Finder::findFiles('*.php')
->filter(fn($file) => strcasecmp($file->read(), 'Nette') === 0);
Filtrowanie w głąb
Podczas wyszukiwania rekurencyjnego możesz ustawić maksymalną głębokość przeglądania za pomocą metody
limitDepth()
. Jeśli ustawisz limitDepth(1)
, przeglądane są tylko pierwsze podkatalogi,
limitDepth(0)
wyłącza przeglądanie w głąb, a wartość –1 znosi limit.
Finder umożliwia za pomocą własnych funkcji decydowanie, do którego katalogu wejść podczas przeglądania. Funkcja
otrzymuje jako parametr obiekt Nette\Utils\FileInfo
i musi zwrócić true
, aby wejść do katalogu:
Finder::findFiles('*.php')
->descentFilter($file->getBasename() !== 'temp');
Sortowanie
Finder oferuje również kilka funkcji do sortowania wyników.
Metoda sortByName()
sortuje wyniki według nazw plików. Sortowanie jest naturalne, czyli poprawnie radzi sobie
z liczbami w nazwach i zwraca np. foo1.txt
przed foo10.txt
.
Finder umożliwia również sortowanie za pomocą własnej funkcji. Otrzymuje ona jako parametr dwa obiekty
Nette\Utils\FileInfo
i musi zwrócić wynik porównania operatorem <=>
, czyli -1
,
0
lub 1
. Na przykład w ten sposób posortujemy pliki według rozmiaru:
$finder->sortBy(fn($a, $b) => $a->getSize() <=> $b->getSize());
Wiele różnych wyszukiwań
Jeśli potrzebujesz znaleźć więcej różnych plików w różnych lokalizacjach lub spełniających inne kryteria, użyj
metody append()
. Zwraca ona nowy obiekt Finder
, więc możliwe jest łączenie wywołań metod w
łańcuch:
($finder = new Finder) // do zmiennej $finder zapisujemy pierwszy Finder!
->files('*.php') // w src/ szukamy plików *.php
->from('src')
->append()
->files('*.md') // w docs/ szukamy plików *.md
->from('docs')
->append()
->files('*.json'); // w bieżącym folderze szukamy plików *.json
Alternatywnie można użyć metody append()
do dodania konkretnego pliku (lub tablicy plików). Wtedy zwraca ten
sam obiekt Finder
:
$finder = Finder::findFiles('*.txt')
->append(__FILE__);
FileInfo
Nette\Utils\FileInfo to klasa reprezentująca plik lub katalog w wynikach wyszukiwania. Jest to rozszerzenie klasy SplFileInfo, która dostarcza informacji takich jak rozmiar pliku, data ostatniej modyfikacji, nazwa, ścieżka itp.
Dodatkowo dostarcza metody do zwracania ścieżki względnej, co jest przydatne podczas przeglądania w głąb:
foreach (Finder::findFiles('*.jpg')->from('.') as $file) {
$absoluteFilePath = $file->getRealPath();
$relativeFilePath = $file->getRelativePathname();
}
Ponadto masz do dyspozycji metody do odczytu i zapisu zawartości pliku:
foreach ($finder as $file) {
$contents = $file->read();
// ...
$file->write($contents);
}
Zwracanie wyników jako tablica
Jak było widać na przykładach, Finder implementuje interfejs IteratorAggregate
, więc możesz użyć
foreach
do przeglądania wyników. Jest zaprogramowany tak, że wyniki są ładowane tylko w trakcie przeglądania,
więc jeśli masz dużą liczbę plików, nie czeka się, aż wszystkie zostaną odczytane.
Wyniki można również otrzymać jako tablicę obiektów Nette\Utils\FileInfo
, za pomocą metody
collect()
. Tablica nie jest asocjacyjna, lecz numeryczna.
$array = $finder->findFiles('*.php')->collect();