Nette Documentation Preview

syntax
Finder: Поиск файлов
********************

.[perex]
Нужно найти файлы, соответствующие определенной маске? Вам поможет программа Finder. Это универсальный и быстрый инструмент для просмотра структуры каталогов.


Установка:

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

В примерах предполагается, что псевдоним уже создан:

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


Использование .[#toc-using]
---------------------------

Сначала посмотрим, как можно использовать [api:Nette\Utils\Finder] для перечисления имен файлов с расширениями `.txt` и `.md` в текущем каталоге:

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

По умолчанию для поиска используется текущий каталог, но вы можете изменить его с помощью методов [in() или from() |#Where to search?].
Переменная `$file` является экземпляром класса [FileInfo |#FileInfo] с множеством полезных методов. Ключ `$name` содержит путь к файлу в виде строки.


Что искать? .[#toc-what-to-search-for]
--------------------------------------

В дополнение к методу `findFiles()` существует также `findDirectories()`, который ищет только в каталогах, и `find()`, который ищет в обоих каталогах. Эти методы статические, поэтому их можно вызывать без создания экземпляра. Параметр mask является необязательным, если вы его не укажете, поиск будет производиться во всех каталогах.

```php
foreach (Finder::find() as $file) {
	echo $file; // теперь все файлы и каталоги перечислены
}
```

Используйте методы `files()` и `directories()`, чтобы добавить, что еще нужно искать. Методы могут вызываться многократно, а в качестве параметра может быть предоставлен массив масок:

```php
Finder::findDirectories('vendor') // все каталоги
	->files(['*.php', '*.phpt']); // плюс все PHP файлы
```

Альтернативой статическим методам является создание экземпляра с помощью `new Finder` (свежий объект, созданный таким образом, ничего не ищет) и указание того, что искать с помощью `files()` и `directories()`:

```php
(new Finder)
	->directories()   // все каталоги
	->files('*.php'); // плюс все файлы PHP
```

Вы можете использовать [подстановочные знаки |#wildcards] `*`, `**`, `?` and `[...]` в маске. Можно даже указывать в каталогах, например, `src/*.php` будет искать все файлы PHP в каталоге `src`.

Симлинки также считаются каталогами или файлами.


Где искать? .[#toc-where-to-search]
-----------------------------------

Директория поиска по умолчанию - это текущая директория. Вы можете изменить это, используя методы `in()` и `from()`. Как видно из названий методов, `in()` ищет только в текущем каталоге, а `from()` ищет и в его подкаталогах (рекурсивно). Если вам нужен рекурсивный поиск в текущем каталоге, вы можете использовать `from('.')`.

Эти методы можно вызывать несколько раз или передавать им несколько путей в виде массивов, тогда поиск файлов будет производиться во всех каталогах. Если один из каталогов не существует, то произойдет ошибка `Nette\UnexpectedValueException`.

```php
Finder::findFiles('*.php')
	->in(['src', 'tests']) // ищет непосредственно в src/ и tests/
	->from('vendor');      // ищет также в подкаталогах vendor/
```

Относительные пути являются относительными по отношению к текущему каталогу. Конечно, можно указать и абсолютные пути:

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

[Символы |#wildcards] подстановки `*`, `**`, `?` can be used in the path. For example, you can use the path `src/*/*.php` для поиска всех файлов PHP в каталогах второго уровня в каталоге `src`. Символ `**`, называемый globstar, является мощным козырем, поскольку позволяет искать и в подкаталогах: используйте `src/**/tests/*.php` для поиска всех файлов PHP в каталоге `tests`, расположенных в `src` или любом из его подкаталогов.

С другой стороны, подстановочные символы `[...]` не поддерживаются в пути, т.е. они не имеют специального значения, чтобы избежать нежелательного поведения в случае, если вы ищете, например, `in(__DIR__)` и случайно в пути появляются символы `[]`.

При глубоком поиске файлов и каталогов сначала возвращается родительский каталог, а затем содержащиеся в нем файлы, что можно изменить на противоположное с помощью `childFirst()`.


Дикие символы .[#toc-wildcards]
-------------------------------

В маске можно использовать несколько специальных символов:

- `*` - replaces any number of arbitrary characters (except `/`)
- `**` - заменяет любое количество произвольных символов, включая `/` (т.е. может осуществляться многоуровневый поиск)
- `?` - replaces one arbitrary character (except `/`)
- `[a-z]` - заменяет один символ из списка символов в квадратных скобках
- `[!a-z]` - заменяет один символ вне списка символов в квадратных скобках

Примеры использования:

- `img/?.png` - файлы с однобуквенным именем `0.png`, `1.png`, `x.png` и т.д.
- `logs/[0-9][0-9][0-9][0-9]-[01][0-9]-[0-3][0-9].log` - файлы журналов в формате `YYYY-MM-DD`
- `src/**/tests/*` - файлы в директории `src/tests`, `src/foo/tests`, `src/foo/bar/tests` и т.д.
- `docs/**.md` - все файлы с расширением `.md` во всех подкаталогах каталога `docs`


Исключая .[#toc-excluding]
--------------------------

Используйте метод `exclude()` для исключения файлов и каталогов из поиска. Вы указываете маску, которой не должен соответствовать файл. Пример поиска файлов `*.txt`, кроме тех, которые содержат букву `X` в имени:

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

Используйте `exclude()`, чтобы пропустить просмотренные подкаталоги:

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


Фильтрация .[#toc-filtering]
----------------------------

Finder предлагает несколько методов фильтрации результатов (т.е. их сокращения). Их можно комбинировать и вызывать многократно.

Используйте `size()` для фильтрации по размеру файла. Таким образом, мы находим файлы размером от 100 до 200 байт:

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

Метод `date()` фильтрует по дате последнего изменения файла. Значения могут быть абсолютными или относительными к текущей дате и времени, например, так можно найти файлы, измененные за последние две недели:

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

Обе функции понимают операторы `>`, `>=`, `<`, `<=`, `=`, `!=`, `<>`.

Finder также позволяет фильтровать результаты с помощью пользовательских функций. Функция получает в качестве параметра объект `Nette\Utils\FileInfo` и должна вернуть `true`, чтобы включить файл в результаты.

Пример: поиск файлов PHP, содержащих строку `Nette` (без учета регистра):

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


Глубинная фильтрация .[#toc-depth-filtering]
--------------------------------------------

При рекурсивном поиске можно задать максимальную глубину проползания с помощью метода `limitDepth()`. Если вы зададите `limitDepth(1)`, то будут просмотрены только первые подкаталоги, `limitDepth(0)` отключает фильтрацию глубины, а значение -1 отменяет ограничение.

Finder позволяет использовать свои собственные функции, чтобы решить, в какой каталог войти при просмотре. Функция получает в качестве параметра объект `Nette\Utils\FileInfo` и должна вернуть `true`, чтобы войти в каталог:

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


Сортировка .[#toc-sorting]
--------------------------

Finder также предлагает несколько функций для сортировки результатов.

Метод `sortByName()` сортирует результаты по имени файла. Сортировка является естественной, т.е. она правильно обрабатывает цифры в именах и возвращает, например, `foo1.txt` перед `foo10.txt`.

Finder также позволяет сортировать с помощью пользовательской функции. Она принимает в качестве параметров два объекта `Nette\Utils\FileInfo` и должна возвращать результат сравнения с оператором `<=>`т.е. `-1`, `0` nebo `1`. Например, так мы сортируем файлы по размеру:

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


Несколько разных поисков .[#toc-multiple-different-searches]
------------------------------------------------------------

Если вам нужно найти несколько разных файлов в разных местах или отвечающих разным критериям, используйте метод `append()`. Он возвращает новый объект `Finder`, поэтому вы можете использовать цепочку вызовов методов:


```php
($finder = new Finder) // сохраняем первый Finder в переменной $finder!
	->files('*.php')   // поиск *.php файлов в src/
	->from('src')
	->append()
	->files('*.md')    // в docs/ ищем файлы *.md
	->from('docs')
	->append()
	->files('*.json'); // в текущей папке ищем файлы *.json
```

В качестве альтернативы вы можете использовать метод `append()` для добавления определенного файла (или массива файлов). Тогда он возвращает тот же объект `Finder`:

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


FileInfo .[#toc-fileinfo]
-------------------------

[Nette\Utils\FileInfo |api:] - это класс, представляющий файл или каталог в результатах поиска. Он является расширением класса [SplFileInfo |php:SplFileInfo], который предоставляет такую информацию, как размер файла, дата последнего изменения, имя, путь и т.д.

Кроме того, он предоставляет методы для возврата относительных путей, что полезно при углубленном поиске:

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

У вас также есть методы для чтения и записи содержимого файла:

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


Возвращение результатов в виде массива .[#toc-returning-results-as-an-array]
----------------------------------------------------------------------------

Как видно из примеров, Finder реализует интерфейс `IteratorAggregate`, поэтому вы можете использовать `foreach` для просмотра результатов. Он запрограммирован так, что результаты загружаются только по мере просмотра, поэтому если у вас большое количество файлов, он не будет ждать, пока все они будут прочитаны.

Вы также можете вернуть результаты в виде массива объектов `Nette\Utils\FileInfo`, используя метод `collect()`. Массив не ассоциативный, а числовой.

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

Finder: Поиск файлов

Нужно найти файлы, соответствующие определенной маске? Вам поможет программа Finder. Это универсальный и быстрый инструмент для просмотра структуры каталогов.

Установка:

composer require nette/utils

В примерах предполагается, что псевдоним уже создан:

use Nette\Utils\Finder;

Использование

Сначала посмотрим, как можно использовать Nette\Utils\Finder для перечисления имен файлов с расширениями .txt и .md в текущем каталоге:

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

По умолчанию для поиска используется текущий каталог, но вы можете изменить его с помощью методов in() или from(). Переменная $file является экземпляром класса FileInfo с множеством полезных методов. Ключ $name содержит путь к файлу в виде строки.

Что искать?

В дополнение к методу findFiles() существует также findDirectories(), который ищет только в каталогах, и find(), который ищет в обоих каталогах. Эти методы статические, поэтому их можно вызывать без создания экземпляра. Параметр mask является необязательным, если вы его не укажете, поиск будет производиться во всех каталогах.

foreach (Finder::find() as $file) {
	echo $file; // теперь все файлы и каталоги перечислены
}

Используйте методы files() и directories(), чтобы добавить, что еще нужно искать. Методы могут вызываться многократно, а в качестве параметра может быть предоставлен массив масок:

Finder::findDirectories('vendor') // все каталоги
	->files(['*.php', '*.phpt']); // плюс все PHP файлы

Альтернативой статическим методам является создание экземпляра с помощью new Finder (свежий объект, созданный таким образом, ничего не ищет) и указание того, что искать с помощью files() и directories():

(new Finder)
	->directories()   // все каталоги
	->files('*.php'); // плюс все файлы PHP

Вы можете использовать подстановочные знаки *, **, ? and [...] в маске. Можно даже указывать в каталогах, например, src/*.php будет искать все файлы PHP в каталоге src.

Симлинки также считаются каталогами или файлами.

Директория поиска по умолчанию – это текущая директория. Вы можете изменить это, используя методы in() и from(). Как видно из названий методов, in() ищет только в текущем каталоге, а from() ищет и в его подкаталогах (рекурсивно). Если вам нужен рекурсивный поиск в текущем каталоге, вы можете использовать from('.').

Эти методы можно вызывать несколько раз или передавать им несколько путей в виде массивов, тогда поиск файлов будет производиться во всех каталогах. Если один из каталогов не существует, то произойдет ошибка Nette\UnexpectedValueException.

Finder::findFiles('*.php')
	->in(['src', 'tests']) // ищет непосредственно в src/ и tests/
	->from('vendor');      // ищет также в подкаталогах vendor/

Относительные пути являются относительными по отношению к текущему каталогу. Конечно, можно указать и абсолютные пути:

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

Символы подстановки *, **, ? can be used in the path. For example, you can use the path src/*/*.php для поиска всех файлов PHP в каталогах второго уровня в каталоге src. Символ **, называемый globstar, является мощным козырем, поскольку позволяет искать и в подкаталогах: используйте src/**/tests/*.php для поиска всех файлов PHP в каталоге tests, расположенных в src или любом из его подкаталогов.

С другой стороны, подстановочные символы [...] не поддерживаются в пути, т.е. они не имеют специального значения, чтобы избежать нежелательного поведения в случае, если вы ищете, например, in(__DIR__) и случайно в пути появляются символы [].

При глубоком поиске файлов и каталогов сначала возвращается родительский каталог, а затем содержащиеся в нем файлы, что можно изменить на противоположное с помощью childFirst().

Дикие символы

В маске можно использовать несколько специальных символов:

  • * – replaces any number of arbitrary characters (except /)
  • ** – заменяет любое количество произвольных символов, включая / (т.е. может осуществляться многоуровневый поиск)
  • ? – replaces one arbitrary character (except /)
  • [a-z] – заменяет один символ из списка символов в квадратных скобках
  • [!a-z] – заменяет один символ вне списка символов в квадратных скобках

Примеры использования:

  • img/?.png – файлы с однобуквенным именем 0.png, 1.png, x.png и т.д.
  • logs/[0-9][0-9][0-9][0-9]-[01][0-9]-[0-3][0-9].log – файлы журналов в формате YYYY-MM-DD
  • src/**/tests/* – файлы в директории src/tests, src/foo/tests, src/foo/bar/tests и т.д.
  • docs/**.md – все файлы с расширением .md во всех подкаталогах каталога docs

Исключая

Используйте метод exclude() для исключения файлов и каталогов из поиска. Вы указываете маску, которой не должен соответствовать файл. Пример поиска файлов *.txt, кроме тех, которые содержат букву X в имени:

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

Используйте exclude(), чтобы пропустить просмотренные подкаталоги:

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

Фильтрация

Finder предлагает несколько методов фильтрации результатов (т.е. их сокращения). Их можно комбинировать и вызывать многократно.

Используйте size() для фильтрации по размеру файла. Таким образом, мы находим файлы размером от 100 до 200 байт:

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

Метод date() фильтрует по дате последнего изменения файла. Значения могут быть абсолютными или относительными к текущей дате и времени, например, так можно найти файлы, измененные за последние две недели:

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

Обе функции понимают операторы >, >=, <, <=, =, !=, <>.

Finder также позволяет фильтровать результаты с помощью пользовательских функций. Функция получает в качестве параметра объект Nette\Utils\FileInfo и должна вернуть true, чтобы включить файл в результаты.

Пример: поиск файлов PHP, содержащих строку Nette (без учета регистра):

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

Глубинная фильтрация

При рекурсивном поиске можно задать максимальную глубину проползания с помощью метода limitDepth(). Если вы зададите limitDepth(1), то будут просмотрены только первые подкаталоги, limitDepth(0) отключает фильтрацию глубины, а значение –1 отменяет ограничение.

Finder позволяет использовать свои собственные функции, чтобы решить, в какой каталог войти при просмотре. Функция получает в качестве параметра объект Nette\Utils\FileInfo и должна вернуть true, чтобы войти в каталог:

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

Сортировка

Finder также предлагает несколько функций для сортировки результатов.

Метод sortByName() сортирует результаты по имени файла. Сортировка является естественной, т.е. она правильно обрабатывает цифры в именах и возвращает, например, foo1.txt перед foo10.txt.

Finder также позволяет сортировать с помощью пользовательской функции. Она принимает в качестве параметров два объекта Nette\Utils\FileInfo и должна возвращать результат сравнения с оператором <=>т.е. -1, 0 nebo 1. Например, так мы сортируем файлы по размеру:

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

Несколько разных поисков

Если вам нужно найти несколько разных файлов в разных местах или отвечающих разным критериям, используйте метод append(). Он возвращает новый объект Finder, поэтому вы можете использовать цепочку вызовов методов:

($finder = new Finder) // сохраняем первый Finder в переменной $finder!
	->files('*.php')   // поиск *.php файлов в src/
	->from('src')
	->append()
	->files('*.md')    // в docs/ ищем файлы *.md
	->from('docs')
	->append()
	->files('*.json'); // в текущей папке ищем файлы *.json

В качестве альтернативы вы можете использовать метод append() для добавления определенного файла (или массива файлов). Тогда он возвращает тот же объект Finder:

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

FileInfo

Nette\Utils\FileInfo – это класс, представляющий файл или каталог в результатах поиска. Он является расширением класса SplFileInfo, который предоставляет такую информацию, как размер файла, дата последнего изменения, имя, путь и т.д.

Кроме того, он предоставляет методы для возврата относительных путей, что полезно при углубленном поиске:

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

У вас также есть методы для чтения и записи содержимого файла:

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

Возвращение результатов в виде массива

Как видно из примеров, Finder реализует интерфейс IteratorAggregate, поэтому вы можете использовать foreach для просмотра результатов. Он запрограммирован так, что результаты загружаются только по мере просмотра, поэтому если у вас большое количество файлов, он не будет ждать, пока все они будут прочитаны.

Вы также можете вернуть результаты в виде массива объектов Nette\Utils\FileInfo, используя метод collect(). Массив не ассоциативный, а числовой.

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