Статьи

Список файлов и каталогов с помощью PHP

В этой статье я расскажу о типичной задаче, с которой вы могли столкнуться при разработке приложения PHP: перечислить файлы и каталоги. Я расскажу о нескольких основных и передовых решениях, каждое из которых имеет свои плюсы и минусы. Сначала я представлю три подхода, в которых используются некоторые базовые функции PHP, а затем перейду к более надежным, использующим итераторы SPL.

В целях обсуждения, давайте предположим, что структура каталогов выглядит следующим образом:

  ---менеджер
 |
 --- пользователь
 |  --- document.txt
 |  --- data.dat
 |  --- style.css
 | --- article.txt
 | --- master.dat
 | --- script.php
 | --- test.dat
 | --- text.txt 

Основные решения

Первый набор подходов демонстрирует использование функций glob() , комбинации функций opendir() , readdir() и closedir() и функции scandir() .

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

Первая функция, которую нужно обсудить, — это glob() которая позволяет нам выполнять поиск путей, используя подстановочные знаки, общие для самых известных оболочек. Функция имеет два параметра:

Давайте посмотрим несколько примеров! Для поиска в каталоге всех файлов и каталогов, заканчивающихся на .txt , вы должны написать:

 <?php $filelist = glob("*.txt"); 

Если вы отобразите $filelist , результат будет:

  массив (
   0 => 'article.txt',
   1 => 'text.txt'
 ) 

Если вам нужен список файлов и каталогов, которые начинаются с «te», напишите код:

 <?php $filelist = glob("te*"); 

Выход:

  массив (
   0 => 'test.dat',
   1 => 'text.txt'
 ) 

Чтобы получить список только каталогов, которые содержат «ma», код:

 <?php $filelist = glob("*ma*", GLOB_ONLYDIR); 

В этом последнем примере вывод:

  массив (
   0 => 'менеджер'
 ) 

Обратите внимание, что последний пример использует константу GLOB_ONLYDIR в качестве необязательного второго параметра. Как видите, файл с именем master.dat исключен из-за этого. Хотя функция glob() проста в использовании, иногда она не так гибка. Например, у него нет флага для извлечения только файлов (а не каталогов), которые соответствуют заданному шаблону.

Использование opendir () и readdir ()

Второй подход к чтению файлов и каталогов, который я хотел бы обсудить, включает функции opendir() , readdir() и closedir() .

opendir() открывает каталог и возвращает дескриптор соединения. Как только дескриптор readdir() , вы можете использовать readdir() . При каждом вызове эта функция будет давать имя следующего файла или каталога внутри открытого каталога. Когда все имена получены, функция возвращает false. Чтобы закрыть ручку, вы используете closedir() .

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

Параллельно с функцией glob() , следующий пример возвращает список всех файлов и каталогов, начинающихся с «te»:

 <?php $filelist = array(); if ($handle = opendir(".")) { while ($entry = readdir($handle)) { if (strpos($entry, "te") === 0) { $filelist[] = $entry; } } closedir($handle); } 

Вывод такой же, как и в предыдущем примере.

Но если вы выполните приведенный выше код и выведите значение $entry во время его работы, вы увидите, что оно иногда содержит несколько странных записей: «.» И «..». Это две виртуальные директории, которые вы найдете в каждой директории файловой системы. Они представляют текущий каталог и родительский каталог (папка верхнего уровня) соответственно.

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

 <?php $filelist = array(); if ($handle = opendir(".")) { while ($entry = readdir($handle)) { if (is_file($entry)) { $filelist[] = $entry; } } closedir($handle); } 

Как вы можете догадаться, использование приведенного выше кода дает следующий вывод:

  массив (
   0 => 'article.txt',
   1 => 'master.dat',
   2 => 'script.php',
   3 => 'test.dat',
   4 => 'text.txt'
 ) 

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

И, наконец, я хотел бы представить scandir() . У него есть только один обязательный параметр: путь для чтения. Возвращаемое значение представляет собой массив файлов и каталогов, содержащихся в пути. Как и в последнем решении, для получения подмножества файлов и каталогов вы должны выполнить пост-фильтрацию самостоятельно. С другой стороны, как видно из приведенного ниже кода, это решение является более кратким и не требует управления дескриптором файла.

В этом примере показано, как получить файлы и каталоги, которые начинаются со строки «te»:

 <?php $entries = scandir("."); $filelist = array(); foreach($entries as $entry) { if (strpos($entry, "te") === 0) { $filelist[] = $entry; } } 

Давайте использовать итераторы SPL

Теперь поговорим о некоторых итераторах SPL . Но прежде чем углубляться в их использование, позвольте мне представить их и библиотеку SPL. SPL предоставляет серию классов для объектно-ориентированных структур данных, итераторов, обработчиков файлов и других функций.

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

Конечно, PHP может предоставить вам ту же информацию, используя такие функции, как filesize() и fileowner() но PHP5 fileowner() свой подход к ООП. Итак, в заключение, мой совет здесь — следовать новым лучшим практикам для языка. Если вам нужна более общая информация об итераторах SPL, взгляните на Использование итераторов SPL .

Как сказано во введении, я покажу использование FilesystemIterator , RecursiveDirectoryIterator и GlobIterator . Первый из них наследуется от DirectoryIterator а остальные наследуются от FilesystemIterator . Все они имеют один и тот же конструктор, который имеет только два параметра:

  • $path (обязательный): путь к элементу файловой системы, который будет повторяться
  • $flags (необязательно): один или несколько флагов, перечисленных в официальной документации

Что действительно отличается в этих итераторах, так это подход, который они используют для навигации по заданному пути.

Файловая системаИтератор

Использовать FilesystemIterator довольно просто. Чтобы увидеть это в действии, я покажу два примера. В первом я буду искать все файлы и каталоги, которые начинаются со строки «te», а во втором будет использоваться другой итератор, RegexIterator , для поиска во всех файлах и каталогах, которые заканчиваются на «t.dat» или «t.php». RegexIterator используется для фильтрации другого итератора на основе регулярного выражения.

 <?php $iterator = new FilesystemIterator("."); $filelist = array(); foreach($iterator as $entry) { if (strpos($entry->getFilename(), "te") === 0) { $filelist[] = $entry->getFilename(); } } 

С кодом выше, результат такой же, как и в предыдущих примерах.

Второй пример, который использует RegexIterator :

 <?php $iterator = new FilesystemIterator("."); $filter = new RegexIterator($iterator, '/t.(php|dat)$/'); $filelist = array(); foreach($filter as $entry) { $filelist[] = $entry->getFilename(); } 

В этом случае вывод:

  массив (
   0 => 'script.php',
   1 => 'test.dat'
 ) 

RecursiveDirectoryIterator

RecursiveDirectoryIterator предоставляет интерфейс для рекурсивной итерации по каталогам файловой системы. Благодаря своей цели он имеет несколько полезных методов, таких как getChildren() и hasChildren() которые возвращают итератор для текущей записи, если это каталог и является ли текущая запись каталогом соответственно. Чтобы увидеть и RecursiveDirectoryIterator и getChildren() в действии, я перепишу последний пример, чтобы получить тот же результат.

 <?php $iterator = new RecursiveDirectoryIterator('.'); $filter = new RegexIterator($iterator->getChildren(), '/t.(php|dat)$/'); $filelist = array(); foreach($filter as $entry) { $filelist[] = $entry->getFilename(); } 

GlobIterator

GlobIterator выполняет GlobIterator по файловой системе аналогично функции glob() . Таким образом, первый параметр может включать подстановочные знаки. Код ниже показывает обычный пример с использованием GlobIterator .

 <?php $iterator = new GlobIterator("te*"); $filelist = array(); foreach($iterator as $entry) { $filelist[] = $entry->getFilename(); } 

Выводы

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

  • Функция glob() представляет собой однострочное решение и позволяет фильтровать, но она не очень гибкая.
  • Решение, использующее opendir() , readdir() и closedir() немного многословно и требует пост-фильтрации, но более гибко.
  • Функция scandir() требует пост-фильтрации, но не требует управления дескриптором.
  • Если вы хотите использовать подход ООП, вы должны использовать библиотеку SPL. Кроме того, вы можете расширить классы в соответствии с вашими потребностями.
  • В то время как GlobIterator имеет возможность выполнять предварительную фильтрацию, другие могут делать то же самое удобным способом с помощью RegexIterator .

Знаете ли вы о других подходах к достижению цели? Если это так, и вы хотите поделиться с нами, продолжайте. Обмен знаниями всегда приветствуется.

Изображение через Fotolia