За чистый и ясный код!

Статьи на тему программирования под веб, используя PHP, MySQL, Jquery и многое другое

Рекурсивный обход директории с помощью итераторов

Ноябрь15
Обращаю Ваше внимание на загородную недвижимость в области, найдите себе достойное жильё!

рекурсия с помощью итераторовКак-то на работе нужно мне было обойти директорию и удалить все файлики в ней, юзать для этого консоль я не мог. Все надо было делать на чистом 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%

Как видно нативные фичи всегда рулят!

Статья просмотренна 68510 раз, зашло посетителей 16477

  

Облако тегов

cli csv dump events form Kohana locale models MySQL mysqldump orm PHP tools trigger validate газ газовый счетчик итоги кеширование переменные

Облако тегов плагина WP Cumulus для WordPress требует для просмотра Flash Player 9 или выше.

Я на твиттере!

  • у твиттера тоже бывают перерывы...

Календарь

Сентябрь 2017
Пн Вт Ср Чт Пт Сб Вс
« Июл    
 123
45678910
11121314151617
18192021222324
252627282930  

Сейчас на сайте