File Search: Finder
Class Nette\Utils\Finder makes browsing the directory structure really easy.
All examples assume the following class alias is defined:
use Nette\Utils\Finder;
Searching for Files
How to find all *.txt files in $dir directory
without recursing subdirectories?
foreach (Finder::findFiles('*.txt')->in($dir) as $key => $file) {
echo $key; // $key is a string containing absolute filename with path
echo $file; // $file is an instance of SplFileInfo
}
If the directory does not exist, an UnexpectedValueException is
thrown.
And what about searching for *.txt files in $dir
including subdirectories? Instead of in(), use
from():
foreach (Finder::findFiles('*.txt')->from($dir) as $file) {
echo $file;
}
Search by more masks, even inside more directories within one iteration:
foreach (Finder::findFiles('*.txt', '*.php')
->in($dir1, $dir2) as $file) {
...
}
Parameters can also be arrays:
foreach (Finder::findFiles($masks)->in($dirs) as $file) {
...
}
Searching for *.txt files containing a number in the name:
foreach (Finder::findFiles('*[0-9]*.txt')->from($dir) as $file) {
...
}
Searching for *.txt files, except those containing
‘X’ in the name:
foreach (Finder::findFiles('*.txt')
->exclude('*X*')->from($dir) as $file) {
...
}
exclude() is specified just after
findFiles(), thus it applies to filename.
Directories to omit can be specified using the exclude
after from clause:
foreach (Finder::findFiles('*.php')
->from($dir)->exclude('temp', '.git') as $file) {
...
}
Here exclude() is after from(), thus
it applies to the directory name.
And now something a bit more complicated: searching for *.txt
files located in subdirectories starting with ‘te’, but not
‘temp’:
foreach (Finder::findFiles('te*/*.txt')
->exclude('temp*/*')->from($dir) as $file) {
...
}
Depth of search can be limited using the limitDepth()
method.
Searching for directories
In addition to files, it is possible to search for directories using
Finder::findDirectories('subdir*'), or to search for files and
directories: Finder::find('file.txt'). In such case, the mask
applies only to files, not directories.
Filtering
You can also filter results. For example by size. This way we will traverse the files of size between 100B and 200B:
foreach (Finder::findFiles('*.php')->size('>=', 100)->size('<=', 200)
->from($dir) as $file) {
...
}
Or files changed in the last two weeks:
foreach (Finder::findFiles('*.php')->date('>', '- 2 weeks')
->from($dir) as $file) {
...
}
Here we traverse PHP files with number of lines greater than 1000. As a filter we use a custom callback:
$finder = Finder::findFiles('*.php')->filter(function($file) {
return count(file($file->getPathname())) > 1000;
})->from($dir);
You can go even further and extend the Nette\Utils\Finder class
for example with a dimensions method using the extension
methods:
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]));
});
});
Finder, find images larger than 50px × 50px:
foreach (Finder::findFiles('*')
->dimensions('>50', '>50')->from($dir) as $file) {
...
}
Connection to Amazon S3
It's possible to use custom streams, for example 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;
}
Handy, right? You will certainly find a use for Finder in your applications.
See also:
Login to submit a comment