Nette Documentation Preview

syntax
Finder: Searching for Files
***************************

.[perex]
Need to find files matching a certain mask? Finder can help you with that. It's a versatile and fast tool for browsing directory structures.


Installation:

```shell
composer require nette/utils
```

The examples assume the following class alias has been created:

```php
use Nette\Utils\Finder;
```


Usage
-----

First, let's see how you can use [api:Nette\Utils\Finder] to list the names of files with the extensions `.txt` and `.md` in the current directory:

```php
foreach (Finder::findFiles(['*.txt', '*.md']) as $name => $file) {
	echo $file;
}
```

The default search directory is the current directory, but you can change it using the [in() or from() |#Where to Search] methods. The `$file` variable is an instance of the [#FileInfo] class, which offers many useful methods. The key `$name` holds the path to the file as a string.


What to Search For?
-------------------

In addition to the `findFiles()` method, there is also `findDirectories()`, which searches only for directories, and `find()`, which searches for both. These methods are static, so they can be called without creating an instance. The mask parameter is optional; if omitted, everything is searched.

```php
foreach (Finder::find() as $file) {
	echo $file; // now all files and directories are listed
}
```

Using the `files()` and `directories()` methods, you can specify additional items to search for. The methods can be called repeatedly, and an array of masks can also be provided as a parameter:

```php
Finder::findDirectories('vendor') // all directories
	->files(['*.php', '*.phpt']); // plus all PHP files
```

An alternative to static methods is creating an instance using `new Finder` (a newly created object like this doesn't search for anything initially) and specifying what to search for using `files()` and `directories()`:

```php
(new Finder)
	->directories()      // all directories
	->files('*.php');    // plus all PHP files
```

You can use [#wildcards] `*`, `**`, `?`, and `[...]` in the mask. You can even specify directories in the mask; for example, `src/*.php` will find all PHP files in the `src` directory.

Symlinks are also treated as directories or files.


Where to Search?
----------------

The default search directory is the current directory. You can change this using the `in()` and `from()` methods. As the method names suggest, `in()` searches only within the specified directory, while `from()` searches its subdirectories as well (recursively). If you want to search recursively in the current directory, you can use `from('.')`.

These methods can be called multiple times, or you can pass multiple paths as an array; files will then be searched in all specified directories. If any of the specified directories do not exist, a `Nette\UnexpectedValueException` is thrown.

```php
Finder::findFiles('*.php')
	->in(['src', 'tests']) // searches directly in src/ and tests/
	->from('vendor');      // also searches in vendor/ subdirectories
```

Relative paths are relative to the current directory. Absolute paths can also be specified, of course:

```php
Finder::findFiles('*.php')
	->in('/var/www/html');
```

You can use [#wildcards] `*`, `**`, `?` in the path. For example, using the path `src/*/*.php`, you can search for all PHP files in second-level directories within the `src` directory. The `**` character, called globstar, is a powerful feature as it allows searching within subdirectories: using `src/**/tests/*.php` searches for all PHP files in the `tests` directory located within `src` or any of its subdirectories.

Conversely, the `[...]` wildcards are not supported in the path, meaning they have no special significance, to prevent unintended behavior if you were searching, for instance, `in(__DIR__)` and the path happened to contain `[]` characters.

When searching files and directories recursively (depth-first), the parent directory is returned first, followed by the files it contains. This order can be reversed using `childFirst()`.


Wildcards
---------

You can use several special characters in the mask:

- `*` - replaces any number of arbitrary characters (except `/`)
- `**` - replaces any number of arbitrary characters including `/` (i.e., allows multi-level searching)
- `?` - replaces one arbitrary character (except `/`)
- `[a-z]` - replaces one character from the list of characters in square brackets
- `[!a-z]` - replaces one character outside the list of characters in square brackets

Usage examples:

- `img/?.png` - files with a single-letter name like `0.png`, `1.png`, `x.png`, etc.
- `logs/[0-9][0-9][0-9][0-9]-[01][0-9]-[0-3][0-9].log` - log files in the format `YYYY-MM-DD`
- `src/**/tests/*` - files in the directories `src/tests`, `src/foo/tests`, `src/foo/bar/tests`, and so on.
- `docs/**.md` - all files with the `.md` extension in all subdirectories of the `docs` directory


Excluding
---------

Using the `exclude()` method, you can exclude files and directories from the search. You specify a mask that the file or directory must not match. Example of searching for `*.txt` files, excluding those containing the letter `X` in their name:

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

To skip specific subdirectories during traversal, use `exclude()`:

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


Filtering
---------

Finder offers several methods for filtering the results (i.e., reducing them). You can combine them and call them repeatedly.

Using `size()`, we filter by file size. This way, we find files with sizes in the range of 100 to 200 bytes:

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

The `date()` method filters by the file's last modification date. Values can be absolute dates or relative to the current date and time. For example, this finds files modified within the last two weeks:

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

Both methods understand the operators `>`, `>=`, `<`, `<=`, `=`, `!=`, `<>`.

Finder also allows filtering results using custom callbacks. The callback receives a `Nette\Utils\FileInfo` object as a parameter and must return `true` for the file to be included in the results.

Example: searching for PHP files containing the string `'Nette'` (case-insensitive):

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


Depth Filtering
---------------

When searching recursively, you can set the maximum traversal depth using the `limitDepth()` method. Setting `limitDepth(1)` traverses only the first level of subdirectories, `limitDepth(0)` disables depth traversal entirely, and a value of -1 removes the depth limit.

Finder allows using custom callbacks to decide which directories to enter during traversal. The callback receives a `Nette\Utils\FileInfo` object representing the directory and must return `true` to enter it:

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


Sorting
-------

Finder also offers several methods for sorting the results.

The `sortByName()` method sorts results by file name. The sorting is natural, meaning it correctly handles numbers in names and returns, e.g., `foo1.txt` before `foo10.txt`.

Finder also allows sorting using a custom callback. It receives two `Nette\Utils\FileInfo` objects as parameters and must return the result of the comparison using the `<=>` operator (i.e., `-1`, `0`, or `1`). For example, this is how we sort files by size:

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


Multiple Different Searches
---------------------------

If you need to find multiple sets of files in different locations or meeting different criteria, use the `append()` method. It returns a new `Finder` object, allowing you to chain method calls for the appended search:


```php
($finder = new Finder) // store the first Finder in the $finder variable!
	->files('*.php')   // search for *.php files in src/
	->from('src')
	->append()
	->files('*.md')    // in docs/ look for *.md files
	->from('docs')
	->append()
	->files('*.json'); // in the current folder look for *.json files
```

Alternatively, the `append()` method can be used to add a specific file (or an array of files). In this case, it returns the same `Finder` object:

```php
$finder = Finder::findFiles('*.txt')
	->append(__FILE__);
```


FileInfo
--------

[api:Nette\Utils\FileInfo] is a class representing a file or directory found in the search results. It extends the [php:SplFileInfo] class and provides information such as file size, last modification date, name, path, etc.

Additionally, it provides methods for returning the relative path, which is useful during recursive traversal:

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

Furthermore, methods are available for reading and writing the file's content:

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


Returning Results as an Array
-----------------------------

As seen in the examples, Finder implements the `IteratorAggregate` interface, so you can use `foreach` to iterate through the results. It's designed so that results are loaded only during iteration, meaning if you have a large number of files, it doesn't wait for all of them to be read beforehand.

You can also retrieve the results as an array of `Nette\Utils\FileInfo` objects using the `collect()` method. The array is numerically indexed, not associative.

```php
$array = $finder->findFiles('*.php')->collect();
```

Finder: Searching for Files

Need to find files matching a certain mask? Finder can help you with that. It's a versatile and fast tool for browsing directory structures.

Installation:

composer require nette/utils

The examples assume the following class alias has been created:

use Nette\Utils\Finder;

Usage

First, let's see how you can use Nette\Utils\Finder to list the names of files with the extensions .txt and .md in the current directory:

foreach (Finder::findFiles(['*.txt', '*.md']) as $name => $file) {
	echo $file;
}

The default search directory is the current directory, but you can change it using the in() or from() methods. The $file variable is an instance of the FileInfo class, which offers many useful methods. The key $name holds the path to the file as a string.

What to Search For?

In addition to the findFiles() method, there is also findDirectories(), which searches only for directories, and find(), which searches for both. These methods are static, so they can be called without creating an instance. The mask parameter is optional; if omitted, everything is searched.

foreach (Finder::find() as $file) {
	echo $file; // now all files and directories are listed
}

Using the files() and directories() methods, you can specify additional items to search for. The methods can be called repeatedly, and an array of masks can also be provided as a parameter:

Finder::findDirectories('vendor') // all directories
	->files(['*.php', '*.phpt']); // plus all PHP files

An alternative to static methods is creating an instance using new Finder (a newly created object like this doesn't search for anything initially) and specifying what to search for using files() and directories():

(new Finder)
	->directories()      // all directories
	->files('*.php');    // plus all PHP files

You can use wildcards *, **, ?, and [...] in the mask. You can even specify directories in the mask; for example, src/*.php will find all PHP files in the src directory.

Symlinks are also treated as directories or files.

The default search directory is the current directory. You can change this using the in() and from() methods. As the method names suggest, in() searches only within the specified directory, while from() searches its subdirectories as well (recursively). If you want to search recursively in the current directory, you can use from('.').

These methods can be called multiple times, or you can pass multiple paths as an array; files will then be searched in all specified directories. If any of the specified directories do not exist, a Nette\UnexpectedValueException is thrown.

Finder::findFiles('*.php')
	->in(['src', 'tests']) // searches directly in src/ and tests/
	->from('vendor');      // also searches in vendor/ subdirectories

Relative paths are relative to the current directory. Absolute paths can also be specified, of course:

Finder::findFiles('*.php')
	->in('/var/www/html');

You can use wildcards *, **, ? in the path. For example, using the path src/*/*.php, you can search for all PHP files in second-level directories within the src directory. The ** character, called globstar, is a powerful feature as it allows searching within subdirectories: using src/**/tests/*.php searches for all PHP files in the tests directory located within src or any of its subdirectories.

Conversely, the [...] wildcards are not supported in the path, meaning they have no special significance, to prevent unintended behavior if you were searching, for instance, in(__DIR__) and the path happened to contain [] characters.

When searching files and directories recursively (depth-first), the parent directory is returned first, followed by the files it contains. This order can be reversed using childFirst().

Wildcards

You can use several special characters in the mask:

  • * – replaces any number of arbitrary characters (except /)
  • ** – replaces any number of arbitrary characters including / (i.e., allows multi-level searching)
  • ? – replaces one arbitrary character (except /)
  • [a-z] – replaces one character from the list of characters in square brackets
  • [!a-z] – replaces one character outside the list of characters in square brackets

Usage examples:

  • img/?.png – files with a single-letter name like 0.png, 1.png, x.png, etc.
  • logs/[0-9][0-9][0-9][0-9]-[01][0-9]-[0-3][0-9].log – log files in the format YYYY-MM-DD
  • src/**/tests/* – files in the directories src/tests, src/foo/tests, src/foo/bar/tests, and so on.
  • docs/**.md – all files with the .md extension in all subdirectories of the docs directory

Excluding

Using the exclude() method, you can exclude files and directories from the search. You specify a mask that the file or directory must not match. Example of searching for *.txt files, excluding those containing the letter X in their name:

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

To skip specific subdirectories during traversal, use exclude():

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

Filtering

Finder offers several methods for filtering the results (i.e., reducing them). You can combine them and call them repeatedly.

Using size(), we filter by file size. This way, we find files with sizes in the range of 100 to 200 bytes:

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

The date() method filters by the file's last modification date. Values can be absolute dates or relative to the current date and time. For example, this finds files modified within the last two weeks:

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

Both methods understand the operators >, >=, <, <=, =, !=, <>.

Finder also allows filtering results using custom callbacks. The callback receives a Nette\Utils\FileInfo object as a parameter and must return true for the file to be included in the results.

Example: searching for PHP files containing the string 'Nette' (case-insensitive):

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

Depth Filtering

When searching recursively, you can set the maximum traversal depth using the limitDepth() method. Setting limitDepth(1) traverses only the first level of subdirectories, limitDepth(0) disables depth traversal entirely, and a value of –1 removes the depth limit.

Finder allows using custom callbacks to decide which directories to enter during traversal. The callback receives a Nette\Utils\FileInfo object representing the directory and must return true to enter it:

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

Sorting

Finder also offers several methods for sorting the results.

The sortByName() method sorts results by file name. The sorting is natural, meaning it correctly handles numbers in names and returns, e.g., foo1.txt before foo10.txt.

Finder also allows sorting using a custom callback. It receives two Nette\Utils\FileInfo objects as parameters and must return the result of the comparison using the <=> operator (i.e., -1, 0, or 1). For example, this is how we sort files by size:

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

Multiple Different Searches

If you need to find multiple sets of files in different locations or meeting different criteria, use the append() method. It returns a new Finder object, allowing you to chain method calls for the appended search:

($finder = new Finder) // store the first Finder in the $finder variable!
	->files('*.php')   // search for *.php files in src/
	->from('src')
	->append()
	->files('*.md')    // in docs/ look for *.md files
	->from('docs')
	->append()
	->files('*.json'); // in the current folder look for *.json files

Alternatively, the append() method can be used to add a specific file (or an array of files). In this case, it returns the same Finder object:

$finder = Finder::findFiles('*.txt')
	->append(__FILE__);

FileInfo

Nette\Utils\FileInfo is a class representing a file or directory found in the search results. It extends the SplFileInfo class and provides information such as file size, last modification date, name, path, etc.

Additionally, it provides methods for returning the relative path, which is useful during recursive traversal:

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

Furthermore, methods are available for reading and writing the file's content:

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

Returning Results as an Array

As seen in the examples, Finder implements the IteratorAggregate interface, so you can use foreach to iterate through the results. It's designed so that results are loaded only during iteration, meaning if you have a large number of files, it doesn't wait for all of them to be read beforehand.

You can also retrieve the results as an array of Nette\Utils\FileInfo objects using the collect() method. The array is numerically indexed, not associative.

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