Удаление директории в PHP

в 19:09, , рубрики: php

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

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

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

Пример реализации:

function recursiveRemoveDir($dir) {

	$includes = glob($dir.'/*');

	foreach ($includes as $include) {

		if(is_dir($include)) {

			recursiveRemoveDir($include);
		}

		else {

			unlink($include);
		}
	}

	rmdir($dir);
}

//Удалим из текущей директории директорию tmp
recursiveRemoveDir('tmp');

Какие проблемы у данного кода?

В простых случаях директория со всеми вложениями удалится. Но предположим, что в нашей директории присутствует скрытый файл, который начинается с точки, например, .htaccess. Функция glob() представляет из себя glob-подстановку UNIX, в которой по умолчанию не участвуют скрытые файлы (DOTFILES). Например, если мы зайдем в nix-терминал и будем использовать bash, то мы столкнемся с той же проблемой (речь именно о скрытых файлах в рамках glob). Для решения вопроса в bash существует команда shopt -s dotglob — она разрешает glob-подстановку скрытых файлов. В PHP же это можно решить добавлением дополнительно параметра GLOB_BRACE для glob() и расширением паттерна из первого параметра функции.

$includes = glob('tmp/{,.}*', GLOB_BRACE);

И тут так же есть проблема — мы столкнемся с бесконечным циклом, так как в массив войдут значения . и .. — что является на системном уровне текущей и родительской директорией. Придем мы к бесконечному циклу (а не к удалению всех родительских директорий), потому что . в рамках сортировки по умолчанию в сформированном массиве будет первее .. — то есть каждый раз мы будет обращаться к одной и той же директории. Для решения проблемы сформируем массив из данных системных директорий и в цикле удалим их индексы из основного массива. Также существует проблема символьных ссылок. Допустим, что в удаляемой директории размещена ссылка на другую директорию, в удалении содержимого которой мы абсолютно не заинтересованы. Для решения проблемы, перед созданием рекурсии, необходимо организовать проверку на то, что сущность не является ссылкой.

В итоге, функция обретает следующий вид:

function recursiveRemoveDir($dir) {

	$includes = glob($dir.'/{,.}*', GLOB_BRACE);
	$systemDots = preg_grep('/.+$/', $includes);

	foreach ($systemDots as $index => $dot) {
		
		unset($includes[$index]);
	}

	foreach ($includes as $include) {

		if(is_dir($include) && !is_link($include)) {

			recursiveRemoveDir($include);
		}

		else {

			unlink($include);
		}
	}

	rmdir($dir);
}

//Удалим из текущей директории директорию tmp
recursiveRemoveDir('tmp');

Код рабочий, но на самом деле можно было сделать и проще. В PHP существует класс FilesystemIterator, который уже по умолчанию имеет необходимые нам настройки. В конструктор передается путь до директории, листинг которой нам нужен. Нам достаточно просто создать объект.

function recursiveRemoveDir($dir) {

	$includes = new FilesystemIterator($dir);

	foreach ($includes as $include) {

		if(is_dir($include) && !is_link($include)) {

			recursiveRemoveDir($include);
		}

		else {

			unlink($include);
		}
	}

	rmdir($dir);
}


//Удалим из текущей директории директорию tmp
recursiveRemoveDir('tmp');

В заключение хотелось бы отметить еще один быстрый способ. Корректность его использования весьма сомнительна — отправить команду на выполнение в SHELL.

system("rm -rf tmp");

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

Автор: Наташа

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js