Рекурсивный обход директории с помощью итераторов
Как-то на работе нужно мне было обойти директорию и удалить все файлики в ней, юзать для этого консоль я не мог. Все надо было делать на чистом php. Удалить просто не пустую директорию, как Вы знаете нельзя, надо удалить всё, что в ней, а потом удалить её.
Для таких целей используется рекурсия.
Я попытался разобраться в этом вопросе, и всё что узнал делюсь.
Сначала я просто попытался пройтись по одной директории не исполmзуя рекурсию разными методами для определения удобства использования и скорости работы.
Проход директории с помощью родных методов
К родным (native) методам я отнес функции opendir(), closedir(), readdir() и rewinddir(). Больше казать об этих ф-циях нечего, чистая классика. Открыли каталог (если он существует), получили дескриптор (указатель) на него и начинаем с ним работать, по окончанию желательно закрыть каталог.
К родным методам я отношу те, которые существует в голом ядре, т.е. если отключить все расширения и эти ф-ции, классы существуют, то я их называю родными или нативными. Например класс или ф-ции по работе с Memcache существуют если подключено соответствующее расширение, следовательно такие вещи родными назвать нельзя.
$dir = 'c:\\windows\\system32'; $odir = opendir($dir); while (($file = readdir($odir)) !== FALSE) { if ($file != '.' && $file != '..') { echo $file.'<br>'; } } closedir($odir);
Проход директории с помощью предопределенного класса dir()
Предопределенных классов довольно много, они или являются родными, как класс dir(), Exception, Reflection, или такие которые подключаются с помощью соот. библиотек — mysqli, curl, GD и тд
$cat = dir($dir); while (($file = $cat->read()) !== FALSE) { if ($file != '.' && $file != '..') { echo $file.'<br>'; } } $cat->close();
Методы класса dir()
Название метода | Описание метода |
path | путь к директории |
handle | ресурс, дескриптор |
close() | закрыть директорию |
rewind() | сброс дескриптора в начало директории |
read() | Чтение одного элемента директории и передвигаем указатель на одну позицию вниз. |
Проход директории с помощью Итератора (DirectoryIterator)
Что есть итератор хорошо описано в Википедии не буду копи-пастить…
Класс DirectoryIterator реализует интерфейс итератора (могут проходить коллекцию в цикле foreach).
$idir = new DirectoryIterator($dir); foreach($idir as $file) { if ($file != '.' && $file != '..') { echo $file->__toString().'<br>'; } }
У данного класса уж очень много методов для работы с файлами и/или директориями. Постараюсь их все здесь описать. Некоторые из приведенных ниже относятся только в Unix подобным системам.
Методы класса DirectoryIterator()
Название метода | Описание метода |
getFilename() | возврат имени файла или поддиректории |
getBasename() | похож на getFilename(), но может удалять суфикс,если таковой передать в виде параметра * |
isDot() | Определяет является ли текущий элемент «.» или «..» |
rewind() | сброс указателя на первый элемент |
valid() | проверка является ли текущий элемент правильным файлом. Честно не понял. |
key() | возврат ключа текущего элемента |
current() | возврат текущего элемента |
next() | на 1 шаг вперед передвигает указатель |
__toString() | оопшный метод, приводит свойство к строке |
getPath() | возврат просто имени директории/файла и все |
getPathname() | возврат пути к файлу/директории+само название |
getPerms() | возврат прав доступа только для UNIX |
getInode() | х.з. что это, судя по названи наверное какое-то имя узла, думаю только для UNIX |
getSize() | размер файла в байтах, для директории всегда ноль |
getOwner() | возврат имя владельца, только для UNIX |
getGroup() | возврат ИД группы, только для UNIX |
getATime() | последний доступ к файлу/директории в сек (начало с 1970) |
getMTime() | последний модификации файла/директории в сек (начало с 1970) |
getCTime() | последний изменения к файла/директории в сек (начало с 1970) |
getType() | возрат dir или file для сотв элемента. |
isWritable() | думаю понятно из названия, возврат истина/ложь |
isReadable() | думаю понятно из названия, возврат истина/ложь |
isExecutable() | думаю понятно из названия, возврат истина/ложь |
isFile() | думаю понятно из названия, возврат истина/ложь |
isDir() | думаю понятно из названия, возврат истина/ложь |
isLink() | думаю понятно из названия, возврат истина/лож,только для UNIX |
getLinkTarget() | для данных методов не нашел описание даже на оф.сайте. |
getRealPath() | для данных методов не нашел описание даже на оф.сайте. |
getFileInfo() | для данных методов не нашел описание даже на оф.сайте. |
getPathInfo() | для данных методов не нашел описание даже на оф.сайте. |
openFile() | для данных методов не нашел описание даже на оф.сайте. |
setFileClass() | для данных методов не нашел описание даже на оф.сайте. |
setInfoClass() | для данных методов не нашел описание даже на оф.сайте. |
* — за подробностями обращайтесь к официальной документации.
Рекурсивный обход директории с помощью родных методов
Рассмотрев как можно пройтись по директории теперь рассмотри как можно пройтись абсолютно по всему каталогу.
function recursive($dir) { static $deep = 0; $odir = opendir($dir); while (($file = readdir($odir)) !== FALSE) { if ($file == '.' || $file == '..') { continue; } else { echo str_repeat('---', $deep).$dir.DIRECTORY_SEPARATOR.$file.'<br>'; } if (is_dir($dir.DIRECTORY_SEPARATOR.$file)) { $deep ++; recursive($dir.DIRECTORY_SEPARATOR.$file); $deep --; } } closedir($odir); } recursive($dir);
При запуске данной ф-ции она пройдет абсолютно по всем (. и .. не включаем по внимание) директориям и файлам и нарисует дерево. Данная функция мне не понравилась, что она сильно громоздкая и я все же больше склоняюсь к ООП.
Рекурсивный обход директории с помощью итератора (RecursiveDirectoryIterator)
Решение для обхода каталога на ООП нашлось и его скрипт ниже.
$rdir = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir), TRUE); foreach ($rdir as $file) { echo str_repeat('---', $rdir->getDepth()).$file.'<br>'; }
Красиво, всего одна строка, два класса и полный набор данных.
Из существующий методов , которые я уже не привожу, т.к. многие уже описал в других классах (key, current, rewind, valid etc), хотелось бы подчеркнуть метод setMaxDepth(integer); — он позволяет задать глубину прохода.
Другие методы которые есть у данного класса даже не описаны на оф. сайте.
Тесты
Как я их проводил. В цикле (1000) я засекал время перед открытие директории и после её закрытия. Затем я высчитывал среднее арифметическое. Т.к. результаты сильно зависят от железа (винчестера и др параметров), то приводить просто время в секундах не кошерно, поэтому я перевел их проценты. За 100% я принял время работы родных функций.
Проход 1000 раз директории $dir = ‘c:\\windows\\system32’;
native — 100%
dir() — 107%
DirectoryIteratior — 115%
Рекурсия директории $dir = ‘c:\\windows\\system32’;
native — 100%
RecursiveDirectoryIterator — 115%
Как видно нативные почти всегда рулят, но и не так далеко отстают ООПшные фичи.
Кто что будет юзать — уж дело личное.
UPD
Прошу прощения у общественности, я не верно провел тест с рекурсивным обходом.
Вот более детальные данные.
native — 100%
RecursiveDirectoryIterator — 185%
Как видно нативные фичи всегда рулят!
Статья просмотренна 97156 раз, зашло посетителей 26848