RSS Мои друзья Контакты

Чтение директорий и поиск файлов в PHP

Для чтения содержимого директории в PHP есть старые проверенные функции readdir, opendir и closedir. Не все до сих пор знают, но в 5 версии появилось нечто более мощное - итераторы. С их помощью рутинная работа по поиску файлов намного упростилась и стала в несколько раз быстрее. Рассмотрим пример, как с помощью итератора прочитать все содержимое каталога

// some flags to filter . and .. and follow symlinks
$flags = FilesystemIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS;
 
// create a simple recursive directory iterator
$iterator = new RecursiveDirectoryIterator($dir, $flags);
 
// make it a truly recursive iterator
$iterator = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::SELF_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD);
 
// iterate over it
foreach ($iterator as $file)
{
  // do something with $file (a SplFileInfo instance)
}

Как видим все просто, нужно только знать когда какой итератор использовать, его разрешенные флаги и как их можно совмещать. Рассмотрим основные флаги

  • FilesystemIterator::SKIP_DOTS - указывает, что нужно пропустить текущую (.) и родительскую директории (..)
  • FilesystemIterator::FOLOW_SYMLINKS - указывает, что нужно идти по символьным ссылкам и выводить их содержимое
  • RecursiveIteratorIterator::SELF_FIRST - указывает на то, что нужно показать сначала родительские элементы, а потом дочерние
  • RecursiveIteratorIterator::CATCH_GET_CHILD - если у итератора не будет прав на чтение какой-либо директории, он просто пропустит ее
  • RecursiveIteratorIterator::LEAVES_ONLY - показывает только файлы
  • RecursiveIteratorIterator::CHILD_FIRST - указывает на то, что нужно показать сначала дочерние элементы, а потом родительские

К сожалению есть небольшая проблема - все слишком объекто-ориентировано. Для обычной фильтрации итераторов нужно создать специальный класс-фильтр. Но не смотря на это итераторы в PHP очень гибкие и мощные. Допустим нужно отфильтровать все html файлы

class OnlyHtmlFilesFilterIterator extends FilterIterator {
  public function accept() {
    $fileinfo = $this->getInnerIterator()->current();
 
    return preg_match('/\.html$/', $fileinfo);
}

В этом специальном классе пишется метод accept, в котором содержится логика отсечения не нужных файлов и директорий. Применить фильтр очень просто

$iterator = new OnlyHtmlFilesFilterIterator($recursiveIterator);

Итераторы в Symfony

В Symfony начиная из первых версий был компонент sfFinder, думаю он многим знакомый. Этот класс очень удобный для поиска файлов и каталогов по заданным критериям. По своей сути он похож на команду find в Linux. А основной плюс, что его можно использовать отдельно от Symfony. Например, что-то вроде этого

sfFinder::type('file') 
  ->name('*Table.class.php')
  ->ignore_version_control()

Интерфейс простой и понятный, возвращает массива файлов и директорий соответствуючих критерию поиска. Но он использует старый механизм opendir, readdir, closedir.

Думаю именно по-этому он был переписан во второй версии Symfony - теперь он использует итераторы и работает быстрее. Поскольку Symfony 2.0 написан на PHP 5.3, то нужно помнить про неймспейсы. Например

use Symfony\Components\Finder\Finder;
 
$finder = new Finder();
$iterator = $finder->files()->in(__DIR__);
 
foreach ($iterator as $file) {
  print $file->getRealpath()."\n";
}

Пример выше выведет рекурсивно на экран все файлы из текущей директории. Почти все методы класса возвращают экземпляр объекта, исключением является метод in, который возвращает итератор для заданного каталога. Рассмотрим пример

$iterator = $finder
  ->files()
  ->name('test.*')
  ->notName('*.rb')
  ->exclude('ruby')
  ->followLinks()
  ->size('>= 1K')
  ->size('<= 2K')
  ->ignoreVCS()
  ->in(__DIR__)
;

Этот код исчет файлы, имя которых test с любым расширением, кроме файлов ruby, идет по символьным ссылкам, фильтрует по размеру и пропускает файлы систем контроля версий (svn например). Фильтровать можно также при помощи анонимной функции

$filter = function (\SplFileInfo $fileinfo) {
  return strlen($fileinfo) > 10);
};
 
$finder
  ->files()
  ->name('*.php')
  ->filter($filter);

Как видим итераторы очень гибкое и мощное дополнение в PHP и доказательством этого есть класс Finder в Symfony 2.0

Добавить комментарий

Комментариев: 0