- Рекурсивный обход директории с помощью итераторов
- Проход директории с помощью родных методов
- Проход директории с помощью предопределенного класса dir()
- Проход директории с помощью Итератора (DirectoryIterator)
- Рекурсивный обход директории с помощью родных методов
- Рекурсивный обход директории с помощью итератора (RecursiveDirectoryIterator)
- Тесты
- 641 10 комментариев http%3A%2F%2Fblog.nagaychenko.com%2F2010%2F11%2F15%2F%25d1%2580%25d0%25b5%25d0%25ba%25d1%2583%25d1%2580%25d1%2581%25d0%25b8%25d0%25b2%25d0%25bd%25d1%258b%25d0%25b9-%25d0%25be%25d0%25b1%25d1%2585%25d0%25be%25d0%25b4-%25d0%25b4%25d0%25b8%25d1%2580%25d0%25b5%25d0%25ba%25d1%2582%25d0%25be%25d1%2580%25d0%25b8%25d0%25b8-iterator%2F %D0%A0%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9+%D0%BE%D0%B1%D1%85%D0%BE%D0%B4+%D0%B4%D0%B8%D1%80%D0%B5%D0%BA%D1%82%D0%BE%D1%80%D0%B8%D0%B8+%D1%81+%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E+%D0%B8%D1%82%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80%D0%BE%D0%B2 2010-11-15+09%3A00%3A14 maxnag http%3A%2F%2Fblog.nagaychenko.com%2F%3Fp%3D641 в “Рекурсивный обход директории с помощью итераторов”
- Как сделать рекурсивный обход массива php
Рекурсивный обход директории с помощью итераторов
Как-то на работе нужно мне было обойти директорию и удалить все файлики в ней, юзать для этого консоль я не мог. Все надо было делать на чистом 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.'
'; > > closedir($odir);
Проход директории с помощью предопределенного класса dir()
Предопределенных классов довольно много, они или являются родными, как класс dir(), Exception, Reflection, или такие которые подключаются с помощью соот. библиотек — mysqli, curl, GD и тд
$cat = dir($dir); while (($file = $cat->read()) !== FALSE) < if ($file != '.' && $file != '..') < echo $file.'
'; > > $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().'
'; > >
У данного класса уж очень много методов для работы с файлами и/или директориями. Постараюсь их все здесь описать. Некоторые из приведенных ниже относятся только в 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.'
'; > 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.'
'; >
Красиво, всего одна строка, два класса и полный набор данных.
Из существующий методов , которые я уже не привожу, т.к. многие уже описал в других классах (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%
Как видно нативные фичи всегда рулят!
Статья просмотренна 95284 раз, зашло посетителей 25986
641 10 комментариев http%3A%2F%2Fblog.nagaychenko.com%2F2010%2F11%2F15%2F%25d1%2580%25d0%25b5%25d0%25ba%25d1%2583%25d1%2580%25d1%2581%25d0%25b8%25d0%25b2%25d0%25bd%25d1%258b%25d0%25b9-%25d0%25be%25d0%25b1%25d1%2585%25d0%25be%25d0%25b4-%25d0%25b4%25d0%25b8%25d1%2580%25d0%25b5%25d0%25ba%25d1%2582%25d0%25be%25d1%2580%25d0%25b8%25d0%25b8-iterator%2F %D0%A0%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9+%D0%BE%D0%B1%D1%85%D0%BE%D0%B4+%D0%B4%D0%B8%D1%80%D0%B5%D0%BA%D1%82%D0%BE%D1%80%D0%B8%D0%B8+%D1%81+%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E+%D0%B8%D1%82%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80%D0%BE%D0%B2 2010-11-15+09%3A00%3A14 maxnag http%3A%2F%2Fblog.nagaychenko.com%2F%3Fp%3D641 в “Рекурсивный обход директории с помощью итераторов”
@terranisu пишет: Конечно все зависит от поставленных задач, но RecursiveDirectoryIterator сильно начинает тормозить при глубине вложения директорий, хотя бы 3. Причем, оставание начинает рости в геометрической прогрессии при увеличении степени вложенности директорий.
Не знаю, как дела обстоят в php5.3.3 (как-то задач подобных не попадалось), но в свое время тестировал на php5.3.1 и все было довольно плохо.
Т.е. если вам не критично время выполнения скрипта и директорий не много, то использование RecursiveDirectoryIterator оправданно. Все просто и лаконично. Иначе, стоит 100 раз подумать прежде, чем использовать.
загадки пишет: спасибо за толковые выкладки. а где можно применить рекурсивные функции подобного рода, кроме как под конкретную задачу?
maxnag пишет: Я всегда её применял для удаления данных в каталоге, х.з. какой уровень вложенности у тебя будет.
Больше я нигде не применял подобного рода ф-ции. В БД при работе с камментами я не юзаю рекурсию, это очень наклдадно, использую другие известные методы.
bosha пишет: Надо различать задачи.
opendir — возвращает список файлов в директории.
RecursiveDirectoryIterator — возвращает ОБЪЕКТЫ. У которых есть свойства (размер, тип, права, владелец, группа и т.д.).
Если необходимо всё это, то костыли с opendir займут куда больше процессорного времени. Рекомендую добавить это в статью, и не вводить людей в заблуждение.
К родным (native) методам я отнес функции opendir(), closedir(), readdir() и rewinddir(). Больше казать об этих ф-циях нечего, чистая классика.
Тут чётко видно, кто есть ф-ция, а кто класс с методами? Что не так с Вашей точки зрения я не так сделал?
bosha пишет: Да, видно. Было бы странно, если бы было непонятно, но проведённые тесты не совсем верны по описаным выше причинам. Попробуйте что нибудь такое сделать на родных методах, и с помощью DirectoryIterator:
https://dl.dropbox.com/u/585714/screenshots/Selec… Результаты, я думаю, Вас удивят.:)
Юрий пишет: Что за дебильная привычка именовать переменные вроде idir? Что в этом сокращении содержится? Мы-ж не криптографы, а программисты. Правильно directoryIterator
maxnag пишет: Это не «дебильная привычка» — это опыт, например я понятие не имею какой тип данных в этой переменной, а по её названию я, иногда, могу понять, что i — это iterator. Также когда юзаешь название класса, у которого есть интерфейс часто пишут IClassName и первая буква означает Interface. Ты можешь называть как угодно, но когда ты работаешь в команде, в большой компании есть этика и разные рекомендации по наименованию всего и вся и этому надо следовать. Рекомендую ознакомиться с этим https://svyatoslav.biz/misc/psr_translation/
Как сделать рекурсивный обход массива php
Для этого можно использовать встроенную функцию array_walk_recursive(). Первым параметром она принимает массив, а вторым колбек, который будет выполнен на каждом элементе массива. Колбек же, в свою очередь, принимает два параметра: элемент и его ключ. Ниже небольшой пример с распечаткой элементов вложенного массива на экран.
$collection = [ 'root' => [ 'key1' => 'value1', 'key2' => 'value2', 'key3' => [ 'key4' => 'value4' ] ] ]; array_walk_recursive($collection, function ($value, $key) var_dump("$key> -> $value>"); >); // => string(14) "key1 -> value1" // => string(14) "key2 -> value2" // => string(14) "key4 -> value4"
Стоит сказать, что если значение ключа содержит массив, то этот ключ не передаётся в колбек. Потому мы и не видим в выводе ключей root и key3 .