Как удалить файл в PHP?

Давайте попробуем решить простую задачу по удалению одного файла или нескольких файлов средствами PHP. В целом, здесь нет ничего сложного, но есть масса деталей, а также вариантов решений, о которых Вам стоит знать. В своей статье я не берусь объять необъятное, но растолковать пару вопросов и привести несколько примеров, думаю, смогу. Приступим?

Как удалить файл средствами PHP?

PHP функция unlink()

А начну я с php-функции unlink(), которая и осуществляет удаление файла:

bool unlink( string $filename [, resource $context ] )

Здесь $filename – путь к файлу, а $context (не обязательно) – описание контекстов для работы с потоками (поддерживается в PHP, начиная с 5.0.0). Функция возвращает TRUE в случае успешного завершения или FALSE в случае возникновения ошибки.

Пример использования php-функции unlink():

$filename = './path/to/file.txt';
if ( !(@unlink($filename)) ) die('Error Delete File.');

Почему эта функция называется unlink()?

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

Примечательно здесь и то, что в Unix можно связать несколько таких точек с одними и теми же данными. Таки данные будут существовать до тех пор, пока существует хотя бы одна ссылка на них. Но если все ссылки будут уничтожены, то и сами данные будут уничтожены. Таким образом, функция unlink() предназначена для удаления именно ссылок, и уже как следствия данных файла.

В Windows такого нет и функция unlink() удаляет именно файл. Более того, некоторые версии PHP для Windows вообще не поддерживают unlink(). В таком случае используется команда del через system() или exec(), например:

$filename = '/2014/04/file.txt';
if ( isset($_ENV['WINDIR']) ) {
  @exec('del '. $filename);
  if ( file_exists($filename) ) die('Error Delete File.');
} else if ( !(@unlink($filename)) ) {
  die('Error Delete File.');
}

В данном случае переменная среды $_ENV['WINDIR'] может служить маркером использования Windows-платформы, а проверка выполнения удаления файла осуществляется через php-функцию file_exists(), которая осуществляет проверку существования файла.

Как удалить файл(ы) в PHP?

В некоторых случаях может получиться и так, что функция unlink() не будет иметь доступа к удалению файла, т.е. получаем ошибку [function.unlink]: permission denied. Это может быть связано с неправильно указанным путём к файлу или отсутствием прав доступа.

Интересным вариантом для работы с путями к файлу является использование php-функций getcwd() (получает имя текущего рабочего каталога) и chdir() (изменяет текущий каталог на указанный), например:

$file_path = 'path/to';
$file_name = '/2014/04/file.txt';
$old = getcwd();
if ( !(@chdir($file_path)) ) die('Error open path.');
if ( !(@unlink($file_name)) ) die('Error Delete File.');
chdir($old);

Как вы видите, так в чём-то проще ориентироваться и отследить проблему. В тоже время, будет не лишним, после выполнения удаления вернуться в текущий каталог $old.

Что же до проблем доступа, то здесь можно попробовать использовать php-функцию chmod() (изменяет режим доступа к файлу), например:

$filename = '/2014/04/path/to/file.txt';
@chmod($filename, 0666);
if ( !(@unlink($filename)) ) die('Error Delete File.');

Если Вам необходимо удалить все файлы в директории удобно использовать комбинацию из php-функций array_map()(применяет callback-функцию ко всем элементам массива) и glob() (находит файловые пути, совпадающие с шаблоном), например:

array_map('unlink', glob("some/dir/*.txt"));

Решение проблемы многопоточности при удалении файлов

Гораздо сложнее, когда речь заходит о крупных сайтах, где велико количество одновременных запросов (потоков) к скрипту. Здесь существует ряд решений. Я же приведу лишь одно из них, основное на использовании семафоров.

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

Для получения ресурса семафора используется функция sem_get(). Функцией можно получить семафор со значением, отличающимся от единицы, и тогда захватить семафор смогут несколько потоков. Для захвата используется функция sem_acquire(). Пример:

$sem = sem_get(1);
if ( sem_acquire($sem) && file_exists($filename) ) @unlink($filename);
sem_remove($sem);

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

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

На этом у меня всё. Надеюсь, что моя статья была Вам полезна или просто познавательно. Спасибо за внимание. Удачи!