Nette Documentation Preview

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

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