Finder: Vyhledávání souborů

Třída Nette\Utils\Finder zjednodušuje procházení adresářovou strukturou na disku.

Všechny příklady předpokládají vytvořený alias:

use Nette\Utils\Finder;

Hledání souborů

Jak vyhledat soubory *.txt v adresáři $dir bez procházení podadresářů?

foreach (Finder::findFiles('*.txt')->in($dir) as $key => $file) {
    echo $key; // $key je řetězec s názvem souboru včetně cesty
    echo $file; // $file je objektem SplFileInfo
}

Jako výsledek hledání získáme instance třídy SplFileInfo.

Pokud by adresář neexistoval, vyhodí se výjimka UnexpectedValueException.

A co třeba hledání souborů *.txt v adresáři $dir a všech jeho podadresářích? Místo in() použijeme from():

foreach (Finder::findFiles('*.txt')->from($dir) as $file) {
    echo $file;
}

Hledání podle více masek a dokonce z více adresářů v rámci jedné iterace:

foreach (Finder::findFiles('*.txt', '*.php')
    ->in($dir1, $dir2) as $file) {
    ...
}

přičemž parametry lze zadat i ve formě polí:

foreach (Finder::findFiles($masks)->in($dirs) as $file) {
    ...
}

Hledání souborů *.txt obsahujících číslici v názvu:

foreach (Finder::findFiles('*[0-9]*.txt')->from($dir) as $file) {
    ...
}

Hledání souborů *.txt kromě těch, co obsahují v názvu písmeno X

foreach (Finder::findFiles('*.txt')
    ->exclude('*X*')->from($dir) as $file) {
    ...
}

exclude() je uvedeno těsně za findFiles(), takže se vztahuje k němu.

Adresáře, kterým se chceme při procházení do hloubky zcela vyhnout, uvedeme v exclude za klauzulí from:

foreach (Finder::findFiles('*.php')
    ->from($dir)->exclude('temp', '.git') as $file) {
    ...
}

Tady se exclude() vztahuje na from().

A teď něco malinko složitějšího: hledání souborů *.txt umístěných v podadresáři začínajícím na te, ale nikoliv temp:

foreach (Finder::findFiles('te*/*.txt')
    ->exclude('temp*/*')->from($dir) as $file) {
    ...
}

Omezit hloubku procházení lze metodou limitDepth().

Hledání adresářů

Kromě souborů lze hledat i adresáře přes Finder::findDirectories('subdir*') nebo obojí Finder::find('file.txt').

Filtrování

Dále je možné výsledky filtrovat. Například podle velikosti. Takto projdeme soubory s velikostí v rozmezí 100B až 200B:

foreach (Finder::findFiles('*.php')->size('>=', 100)->size('<=', 200)
    ->from($dir) as $file) {
    ...
}

Nebo soubory změněné v posledních dvou týdnech:

foreach (Finder::findFiles('*.php')->date('>', '- 2 weeks')
    ->from($dir) as $file) {
    ...
}

Zde se prochází soubory PHP s počtem řádku větším než 1000. Jako filtr použijeme vlastní callback:

$finder = Finder::findFiles('*.php')->filter(function($file) {
    return count(file($file->getPathname())) > 1000;
})->from($dir);

Lze jít ještě dál a třídu Nette\Utils\Finder za využití rozšiřujících metod rozšířit například o metodu dimensions:

Finder::extensionMethod('dimensions', function($finder, $width, $height){
    if (!preg_match('#^([=<>!]+)\s*(\d+)$#i', $width, $mW)
        || !preg_match('#^([=<>!]+)\s*(\d+)$#i', $height, $mH)
    ) {
        throw new InvalidArgumentException('Invalid dimensions predicate format.');
    }
    return $finder->filter(function($file) use ($mW, $mH) {
        return $file->getSize() >= 12 && ($size = getimagesize($file->getPathname()))
            && (!$mW || Finder::compare($size[0], $mW[1], $mW[2]))
            && (!$mH || Finder::compare($size[1], $mH[1], $mH[2]));
    });
});

Findere, najdi obrázky s rozměry většími než 50px × 50px:

foreach (Finder::findFiles('*')
    ->dimensions('>50', '>50')->from($dir) as $file) {
    ...
}

Propojení na Amazon S3

Lze také využít uživatelské streamy, kupříkladu Zend_Service_Amazon_S3:

$s3 = new Zend_Service_Amazon_S3($key, $secret);
$s3->registerStreamWrapper('s3');

foreach (Finder::findFiles('photos*')
    ->size('<=', 1e6)->in('s3://bucket-name') as $file) {
    echo $file;
}

Šikovné, že? Finder určitě najde využití ve vašich aplikacích.