Nette Documentation Preview

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

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


Установка:

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

Примеры предполагают, что создан псевдоним:

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


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

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

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

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


Что искать?
-----------

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

```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] `*`, `**`, `?` и `[...]`. Вы даже можете указать каталоги, например `src/*.php` найдет все PHP-файлы в каталоге `src`.

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


Где искать?
-----------

Каталог по умолчанию для поиска — текущий каталог. Вы измените его с помощью методов `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] `*`, `**`, `?`. Так, например, с помощью пути `src/*/*.php` вы можете искать все PHP-файлы в каталогах второго уровня в каталоге `src`. Символ `**`, называемый globstar, является мощным козырем, потому что позволяет искать и в подкаталогах: с помощью `src/**/tests/*.php` вы ищете все PHP-файлы в каталоге `tests`, находящемся в `src` или любом его подкаталоге.

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

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


Метасимволы (Wildcards)
-----------------------

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

- `*` - заменяет любое количество любых символов (кроме `/`)
- `**` - заменяет любое количество любых символов, включая `/` (т.е. можно искать многоуровнево)
- `?` - заменяет один любой символ (кроме `/`)
- `[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`:

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

Для пропуска обходимых подкаталогов используйте `exclude()`:

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


Фильтрация
----------

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);
```


Фильтрация в глубину
--------------------

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

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

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


Сортировка
----------

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

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

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

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


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

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


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

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

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


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);
}
```


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

Как было видно в примерах, 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(), который ищет и то, и другое. Эти методы статические, поэтому их можно вызывать без создания экземпляра. Параметр с маской необязателен, если его не указать, будет найдено все.

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-файлы

В маске можно использовать метасимволы *, **, ? и [...]. Вы даже можете указать каталоги, например 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');

В пути можно использовать метасимволы *, **, ?. Так, например, с помощью пути src/*/*.php вы можете искать все PHP-файлы в каталогах второго уровня в каталоге src. Символ **, называемый globstar, является мощным козырем, потому что позволяет искать и в подкаталогах: с помощью src/**/tests/*.php вы ищете все PHP-файлы в каталоге tests, находящемся в src или любом его подкаталоге.

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

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

Метасимволы (Wildcards)

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

  • * – заменяет любое количество любых символов (кроме /)
  • ** – заменяет любое количество любых символов, включая / (т.е. можно искать многоуровнево)
  • ? – заменяет один любой символ (кроме /)
  • [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(fn($file) => $file->getBasename() !== 'temp');

Сортировка

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

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

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

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

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

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

($finder = new Finder) // в переменную $finder мы сохраним первый Finder!
	->files('*.php')   // в src/ ищем файлы *.php
	->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();