Обзор компонентов Symfony2 : Finder

Установка

Процесс установки невероятно прост при помощи Composer:

{
    "require": {
        "symfony/finder": "2.4.*"
    }
}

Если вы никогда не пользовались Composer, то здесь вы можете ознакомиться с ним.

Компонент

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

<?php

use Symfony\Component\Finder\Finder;

include_once __DIR__. '/vendor/autoload.php';

$finder = new Finder();
$finder->in(__DIR__)->files();

foreach ($finder as $file) {
    echo $file->getRealpath() . PHP_EOL;
}

В этом примере мы создаем экземпляр класса Finder и настраиваем его для поиска только в текущем каталоге используя метод files(). Затем, так как Finder реализует итератор, мы можем пройтись по коллекции найденных файлов с помощью цикла foreach. В примере мы выводим полный путь для каждого найденного файла.

Вместо использования цикла foreach, мы можем воспользоваться другими PHP функциями для работы с итераторами, например iterator_to_array и iterator_count.

<?php

$filesNumber = iterator_count($finder);
$files = iterator_to_array($finder);

var_dump($filesNumber, $files);

Если в текущем каталоге находится только один PHP файл, то на выходе мы получим следующий вывод.

<?php

int(1)
array(1) {
  '/Users/raulfraile/Sites/sgposts/finder/index.php' =>
  class Symfony\Component\Finder\SplFileInfo#13 (4) {
    private $relativePath =>
    string(0) ""
    private $relativePathname =>
    string(9) "index.php"
    private $pathName =>
    string(48) "/Users/raulfraile/Sites/sgposts/finder/index.php"
    private $fileName =>
    string(9) "index.php"
  }
}

Фильтрация

В официальной документации компонента приведен огромный список возможных фильтров для поиска файлов и директорий. Не вижу смысла описывать все способы фильтрации, рассмотрим только самые распространённые:

  • in($dirs): расположение - единственный обязательный критерий. Можно задать как один, так и несколько каталогов.
  • files() / directories(): искать только файлы или директории
  • depth(): ограничение глубины поиска. Можно задавать параметры в виде '==0' или '<3'
  • exclude($dirs): исключение определённых каталогов
  • ignoreVCS($ignoreVCS): пропуск директорий систем контроля версий .git
  • name($pattern): поиск по заданному паттерну. Паттерн может быть в формате glob или регулярных выражений
  • contains($pattern): поиск файлов по содержимому. В качестве паттерна можно использовать строковое значение и регулярные выражения
  • filter($closure) / sort($closure): изменение метода фильтрации данных

Внутренности

Адаптеры

Компонент основан на шаблоне проектирования Adapter и позволяет искать файлы независимо от операционной системы. Изначально доступно три адаптера: PhpAdapter, GnuFindAdapter и BsdFindAdapter.

PhpAdapter всегда доступен и выполняет поиск при помощи итераторов PHP, в то время как GnuFindAdapter и BsdFindAdapter доступны только на операционных системах семейства Unix. Они улучшают производительность за счет использования таких команд как "find", "sort", "cut" или "grep". Вот например, созданные команды для некоторых общих критериев, использующих BsdFindAdapter.

<?php

// find  -E '/dir' -noleaf -mindepth 1 -not \( -regex '.*(^|/)\..+(/|$).*' \)
$finder->in(__DIR__);

// find  -E '/dir' -noleaf -mindepth 1 -maxdepth 1 -type f -not \( -regex '.*(^|/)\..+(/|$).*' \)
$finder->in(__DIR__)->files()->depth(0);

// find  -E '/dir' -noleaf -mindepth 1
$finder->in(__DIR__)->ignoreDotFiles(false);

// find  -E '/dir' -noleaf -mindepth 1 -type f \( -name '*.php' \) -not \( -regex '.*(^|/)\..+(/|$).*' \)
$finder->in(__DIR__)->files()->name('*.php');

// find  -E '/dir' -noleaf -mindepth 1 -type f -not \( -regex '.*(^|/)\..+(/|$).*' \) | grep -v '^$' | xargs -I{} grep -I -l -Ee 'hello' {}
$finder->in(__DIR__)->files()->contains('hello');

Все адаптеры используют интерфейс AdapterInterface, в котором заданы методы для определения критериев поиска. Есть два наиболее важных метода, с помощью которых Finder выбирает соответствующий адаптер: isSupported() и getName(). Если адаптер наследуют AbstractAdapter, то следует реализовать метод canBeUsed(), а не isSupported(), так как базовая реализация поддерживает уровень кеша.

Интерфейс IteratorAggregate

Как мы уже видели, экземпляр класса Finder реализует интерфейсы IteratorAggregate и Countable.

Реализовать интерфейс Countable довольно просто, необходимо описать только метод count(). Этот метод вызывается при обращении к встроенному методу count(), но не при использовании iterator_count().

Интерфейс IteratorAggregate, также описывает только один метод - generator(), который должен вернуть объект реализующий Traversable интерфейс. Как только мы задействуем Finder в foreach цикле, либо в качестве параметра в функциях iterator_count() или iterator_to_array(), вызывается метод generator() и создаётся новый итератор. Так же важно, что при поиске в разных местоположениях (переданных через метод in()), создаётся экземпляр AppendIterator, содержащий в себе итераторы для каждой директории, а затем поочередно выполняется поиск.

SplFileInfo

Для получения информации об отдельном файле компонент Finder использует класс SplFileInfo, он добавляет возможность использования относительных путей (relative paths) и прямого доступа к файлам при помощи методов getRelativePath(), getRelativePathname() и getContents().

Glob

Glob находит своё применение в операционных системах семейства Unix для поиска по патернам. То есть запрос “*.php” выдаст все файлы с расширением php. Но PHP полноцено не поддерживает такую функцию (нет поддержки работы с удалёнными файлами). Сам компонент решает эту проблему следующим путем - он автоматически конвертирует запрос в регулярное выражение (правда с определёнными потерями скорости). Так что при использовании PhpAdapter в любом случае вы работаете с регулярными выражениями.

Например:

<?php

$finder->in(__DIR__)->files()->name('*.php');
// генерирует:
// "^(?=[^\.])[^/]*\.php$"

Где используется этот компонент?

Как я уже говорил, согласно Packagist - это один из самых часто используемых PHP пакетов. Поэтому нетрудно посмотреть статистику и выделить основные проекты, где используется этот компонент.

  • Composer
  • Sculpin
  • phpBB
  • Ladybug
  • Spress
  • easybook

Статьи из серии