В этой статье я расскажу о типичной задаче, с которой вы могли столкнуться при разработке приложения 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()
которая позволяет нам выполнять поиск путей, используя подстановочные знаки, общие для самых известных оболочек. Функция имеет два параметра:
-
$pattern
(обязательный): шаблон поиска -
$flags
(необязательно): один или несколько флагов, перечисленных в официальной документации
Давайте посмотрим несколько примеров! Для поиска в каталоге всех файлов и каталогов, заканчивающихся на .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