Finder: vyhledávání souborů
Potřebujete najít soubory vyhovující určité masce? Finder vám v tom pomůže. Je to všestranný a rychlý nástroj pro procházení adresářové struktury.
Instalace:
composer require nette/utils
Příklady předpokládají vytvořený alias:
use Nette\Utils\Finder;
Použití
Nejprve si ukážeme, jak můžete pomocí Nette\Utils\Finder vypsat jména souborů s příponami
.txt
a .md
v aktuálním adresáři:
foreach (Finder::findFiles(['*.txt', '*.md']) as $name => $file) {
echo $file;
}
Výchozí adresář pro hledání je aktuální adresář, ale můžete ho změnit pomocí metod in() nebo from(). Proměnná $file
je instancí třídy FileInfo se spoustou užitečných metod. V klíči $name
je cesta k souboru jako
řetězec.
Co se má hledat?
Kromě metody findFiles()
existuje i findDirectories()
, která hledá jen adresáře, a
find()
, která hledá obojí. Tyto metody jsou statické, takže je lze volat bez vytvoření instance. Parameter
s maskou je volitelný, pokud ho neuvedete, vyhledá se vše.
foreach (Finder::find() as $file) {
echo $file; // nyní se vypíší všechny soubory i adresáře
}
Pomocí metod files()
a directories()
můžete doplňovat co dalšího se má vyhledat. Metody lze
volat opakovaně a jako parametr lze uvést i pole masek:
Finder::findDirectories('vendor') // všechny adresáře
->files(['*.php', '*.phpt']); // plus všechny PHP soubory
Alternativou statických metod je vytvoření instance pomocí new Finder
(takto vytvořený čerstvý objekt
nevyhledává nic) a uvedení co hledat pomocí files()
a directories()
:
(new Finder)
->directories() // všechny adresáře
->files('*.php'); // plus všechny PHP soubory
V masce můžete používat zástupné znaky *
, **
, ?
a [...]
. Dokonce můžete specifikovat i v adresáře, například src/*.php
vyhledá všechny PHP
soubory v adresáři src
.
Symlinky jsou také považovány za adresáře nebo soubory.
Kde se má hledat?
Výchozí adresář pro hledání je aktuální adresář. Změníte ho pomocí metod in()
a from()
.
Jak je z názvů metod patrné, in()
hledá pouze v daném adresáři, zatímco from()
hledá
i v jeho podadresářích (rekurzivně). Pokud chcete vyhledávat rekurzivně v aktuálním adresáři, můžete použít
from('.')
.
Tyto metody lze volat vícekrát nebo jim předat více cest jakožto pole, soubory se pak budou hledat ve všech
adresářích. Pokud některý z adresářů neexistuje, vyhodí se výjimka Nette\UnexpectedValueException
.
Finder::findFiles('*.php')
->in(['src', 'tests']) // hledá přímo v src/ a tests/
->from('vendor'); // hledá i v podadresářích vendor/
Relativní cesty jsou relativní vůči aktuálnímu adresáři. Lze samozřejme uvést i absolutní cesty:
Finder::findFiles('*.php')
->in('/var/www/html');
V cestě je možné použít zástupné znaky zástupné znaky *
,
**
, ?
. Můžete tak třeba pomocí cesty src/*/*.php
hledat všechny PHP soubory
v adresářích druhé úrovně v adresáři src
. Znak **
nazývaný globstar je mocným trumfem,
protože umožňuje hledat i v podadresářích: pomocí src/**/tests/*.php
hledáte všechny PHP soubory
v adresáři tests
nacházejícím se v src
nebo jakémkoliv jeho podadresáři.
Naopak zástupné znaky [...]
v cestě podporovány nejsou, tj. nemají speciální význam, aby nedocházelo
k nežádoucímu chování v případě, že budete hledat třeba in(__DIR__)
a náhodou v cestě se budou
vyskytovat znaky []
.
Při vyhledávání souborů i adresáře do hloubky se vrací nejprve rodičovský adresář a teprve poté soubory v něm
obsažené, což lze obrátit pomocí childFirst()
.
Zástupné znaky
V masce můžete používat několik speciálních znaků:
*
– nahrazuje libovolný počet libovolných znaků (kromě/
)**
– nahrazuje libovolný počet libovolných znaků včetně/
(tj. lze hledat víceúrovňově)?
– nahrazuje jeden libovolný znak (kromě/
)[a-z]
– nahrazuje jeden znak ze seznamu znaků v hranatých závorkách[!a-z]
– nahrazuje jeden znak mimo seznam znaků v hranatých závorkách
Příklady použití:
img/?.png
– soubory s jednopísmenným názvem0.png
,1.png
,x.png
, atd.logs/[0-9][0-9][0-9][0-9]-[01][0-9]-[0-3][0-9].log
– logy ve formátuYYYY-MM-DD
src/**/tests/*
– soubory v adresářisrc/tests
,src/foo/tests
,src/foo/bar/tests
a tak dále.docs/**.md
– všechny soubory s příponou.md
ve všech podadresářích adresáředocs
Vyloučení
Pomocí metody exclude()
lze vyloučít soubory a adresáře z vyhledávání. Uvedete masku, které soubor
nesmí vyhovovat. Příklad hledání souborů *.txt
kromě těch, co obsahují v názvu písmeno
X
:
Finder::findFiles('*.txt')
->exclude('*X*');
Vynechání procházených podadresářů použijte exclude()
:
Finder::findFiles('*.php')
->from($dir)
->exclude('temp', '.git')
Filtrování
Finder nabízí několik metod pro filtrování výsledků (tj. jejich redukci). Můžete je kombinovat a volat opakovaně.
Pomocí size()
filtrujeme podle velikosti souboru. Takto najdeme soubory s velikostí v rozmezí 100 až
200 bytů:
Finder::findFiles('*.php')
->size('>=', 100)
->size('<=', 200);
Metoda date()
filtruje podle data poslední změny souboru. Hodnoty mohou být absolutní nebo relativní
k aktuálnímu datu a času, například takto najdeme soubory změněné v posledních dvou týdnech:
Finder::findFiles('*.php')
->date('>', '-2 weeks')
->from($dir)
Obě funkce rozumí operátorům >
, >=
, <
, <=
, =
,
!=
, <>
.
Finder umožňuje také filtrovat výsledky pomocí vlastních funkcí. Funkce dostane jako parametr objekt
Nette\Utils\FileInfo
a musí vrátit true
, aby byl soubor zahrnut do výsledků.
Příklad: hledání souborů PHP, které obsahují řetězec Nette
(bez ohledu na velikost písmen):
Finder::findFiles('*.php')
->filter(fn($file) => strcasecmp($file->read(), 'Nette') === 0);
Filtrování do hloubky
Při rekurzivním vyhledávání můžete nastavit maximální hloubku procházení pomocí metody limitDepth()
.
Pokud nastavíte limitDepth(1)
, prochází se pouze první podadresáře, limitDepth(0)
vypne
procházení do hloubky a hodnota –1 ruší limit.
Finder umožňuje pomocí vlastních funkcí rozhodovat, do kterého adresáře se má při procházení vstupit. Funkce
dostane jako parametr objekt Nette\Utils\FileInfo
a musí vrátit true
, aby se do adresáře
vstoupilo:
Finder::findFiles('*.php')
->descentFilter($file->getBasename() !== 'temp');
Řazení
Finder nabízí také několik funkcí pro řazení výsledků.
Metoda sortByName()
řadí výsledky podle názvů souborů. Řazení je naturální, tedy správně si poradí
s čísly v názvech a vrací např. foo1.txt
před foo10.txt
.
Finder umožňuje také řadit pomocí vlastní funkce. Ta dostane jako parametr dva objekty Nette\Utils\FileInfo
a musí vrátit výsledek porovnání operátorem <=>
, tedy -1
, 0
nebo
1
. Například takto seřadíme soubory podle velikosti:
$finder->sortBy(fn($a, $b) => $a->getSize() <=> $b->getSize());
Více různých hledání
Pokud potřebujete najít více různých souborů v různých lokacích nebo splňujích jiná kritéria, použijte metodu
append()
. Vrací nový objekt Finder
, takže je možné řetězit volání metod:
($finder = new Finder) // do proměnné $finder si uložíme první Finder!
->files('*.php') // v src/ hledáme soubory *.php
->from('src')
->append()
->files('*.md') // v docs/ hledáme soubory *.md
->from('docs')
->append()
->files('*.json'); // v aktuální složce hledáme soubory *.json
Alternativně lze použít metodu append()
pro přidání konkrétního souboru (nebo pole souborů). Pak vrací
ten samý objekt Finder
:
$finder = Finder::findFiles('*.txt')
->append(__FILE__);
FileInfo
Nette\Utils\FileInfo je třída reprezentující soubor nebo adresář ve výsledků hledání. Jde o rozšíření třídy SplFileInfo, která poskytuje informace, jako je velikost souboru, datum poslední změny, jméno, cesta, atd.
Navic poskytuje metody pro vrácení relativní cesty, což je užitečné při procházení do hloubky:
foreach (Finder::findFiles('*.jpg')->from('.') as $file) {
$absoluteFilePath = $file->getRealPath();
$relativeFilePath = $file->getRelativePathname();
}
Dále máte k dispozici metody pro přečtení a zápis obsahu souboru:
foreach ($finder as $file) {
$contents = $file->read();
// ...
$file->write($contents);
}
Vrácení výsledků jako pole
Jak bylo vidět v příkladech, Finder implementuje rozhraní IteratorAggregate
, takže můžete použít
foreach
pro procházení výsledků. Je naprogramovaný tak, že výsledky jsou načítány pouze v průběhu
procházení, takže pokud máte velké množství souborů, nečeká se, než se všechny přečtou.
Výsledky si můžete nechat také vrátit jako pole objektů Nette\Utils\FileInfo
, a to metodou
collect()
. Pole není asociativní, ale numerické.
$array = $finder->findFiles('*.php')->collect();