How do I delete a directory and its entire contents (files and subdirectories) in PHP?
-
Here is good approach & solution to: https://stackoverflow.com/a/15111679/2377343 – T.Todua Dec 23 '20 at 14:07
21 Answers
The user-contributed section in the manual page of rmdir
contains a decent implementation:
function rrmdir($dir) {
if (is_dir($dir)) {
$objects = scandir($dir);
foreach ($objects as $object) {
if ($object != "." && $object != "..") {
if (is_dir($dir. DIRECTORY_SEPARATOR .$object) && !is_link($dir."/".$object))
rrmdir($dir. DIRECTORY_SEPARATOR .$object);
else
unlink($dir. DIRECTORY_SEPARATOR .$object);
}
}
rmdir($dir);
}
}
-
1@The Pixel Developer - I added [an answer](http://stackoverflow.com/questions/3338123/how-do-i-recursively-delete-a-directory-and-its-entire-contents-filessub-dirs/3352564#3352564) showing that. – salathe Jul 28 '10 at 11:58
-
2check out the solution someone gave me for the same question: glob seems to work nicer: http://stackoverflow.com/questions/11267086/php-unlink-all-files-withing-a-directory-and-then-deleting-that-directory – rolling_codes Aug 10 '12 at 17:09
-
This calls `is_dir` twice for each recursed directory. If the argument is a symlink, it also follows it instead of deleting the symlink, which might or might not be what you want. In any case, it's not what `rm -rf` does. – Vladimir Panteleev Feb 11 '20 at 07:53
-
1Instantly and permanently disbelieve anyone who claims they can implement recursive directory deletion in PHP in under 200 lines of code and handle ALL of the dozens corner cases on all operating systems on which PHP can run. – Szczepan Hołyszewski Jun 20 '21 at 18:15
-
This isn't really good recursion, or rather a good use case; moreover nothing really is.. `Recursion is generally bad practice in php` as it doesn't support [tail call optimization](https://stackoverflow.com/questions/6171807/does-php-optimize-tail-recursion). Most interpreted languages do not. Recursion generally is to help divide large memory problems as well as manage stack frames. In this implementation your waisting stack frames where as examples with `RecursiveIteratorIterator` will not. – Richard Tyler Miles Mar 04 '22 at 03:22
-
Using built-in iterators will utilize php's C foundations. Thus, allowing recursion to be effective. Notably you can write C code alongside your PHP using PHP's newish [FFI](https://www.php.net/manual/en/class.ffi.php). – Richard Tyler Miles Mar 04 '22 at 03:27
Building on The Pixel Developer's comment, a snippet using the SPL might look like:
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($files as $fileinfo) {
$todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink');
$todo($fileinfo->getRealPath());
}
rmdir($dir);
Note: It does no sanity checking and makes use of the SKIP_DOTS flag introduced with the FilesystemIterator in PHP 5.3.0. Of course, the $todo
could be an if
/else
. The important point is that CHILD_FIRST
is used to iterate over the children (files) first before their parent (folders).
-
-
1Thank you. Also: shouldn't you be using the `getPathname()` method instead of `getRealPath()`? – Alix Axel Jun 01 '11 at 08:19
-
Use whichever does the job for your particular needs, the answer is only a generic example of iterating over the files first then their respective folders. – salathe Jun 01 '11 at 09:26
-
4This solution works well, however it deletes everything... except the directory (whether empty or not). There should an `rmdir($dir)` at the end of the script. – laurent Jan 10 '14 at 15:22
-
4[Here is the same function](https://gist.github.com/mindplay-dk/a4aad91f5a4f1283a5e2) unwrapped, doc-blocked, and made consistent with `rmdir()` and `unlink()`, e.g. aborts with `E_WARNING` and returns `true` or `false` indicating success. – mindplay.dk Jul 03 '14 at 14:10
-
[FilesystemIterator](http://php.net/manual/de/class.filesystemiterator.php) will basically do the same thing as `$files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST );` – dbf Mar 01 '16 at 10:01
-
3@dbf no it won't, the `FilesystemIterator` is not a recursive iterator. – salathe Mar 01 '16 at 20:09
-
Deletes all files and folders in the path.
function recurseRmdir($dir) {
$files = array_diff(scandir($dir), array('.','..'));
foreach ($files as $file) {
(is_dir("$dir/$file") && !is_link("$dir/$file")) ? recurseRmdir("$dir/$file") : unlink("$dir/$file");
}
return rmdir($dir);
}

- 2,130
- 19
- 20
-
1
-
9Please note that this is not symlink safe! You need a sanity check after is_dir to also check that it's !is_link, because otherwise you can symlink to an external folder which then is deleted and this may be considered a security hole. So you should change `is_dir("$dir/$file")` to `is_dir("$dir/$file") && !is_link("$dir/$file")` – Kira M. Backes Jul 04 '17 at 08:57
-
The real problem with this code is that php doesn't support recursion, aka [tail call optimization](https://stackoverflow.com/questions/6171807/does-php-optimize-tail-recursion). Most interpreted languages do not – Richard Tyler Miles Mar 04 '22 at 02:41
-
For *nix you can use a shell_exec
for rm -R
or DEL /S folder_name
for Windows.

- 24,706
- 6
- 50
- 73

- 11,961
- 7
- 52
- 75
-
3
-
-
2@WiR3D so long as the exec command contains no user input, you should be good. Ex: `exec('rm -rf ' . __DIR__ . '/output/*.log');` – Brian Hannay Apr 12 '18 at 19:42
There is another thread with more examples here: A recursive remove directory function for PHP?
If you are using Yii then you can leave it to the framework:
CFileHelper::removeDirectory($my_directory);

- 1
- 1

- 10,639
- 3
- 49
- 62
<?php
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use SplFileInfo;
# http://stackoverflow.com/a/3352564/283851
# https://gist.github.com/XzaR90/48c6b615be12fa765898
# Forked from https://gist.github.com/mindplay-dk/a4aad91f5a4f1283a5e2
/**
* Recursively delete a directory and all of it's contents - e.g.the equivalent of `rm -r` on the command-line.
* Consistent with `rmdir()` and `unlink()`, an E_WARNING level error will be generated on failure.
*
* @param string $source absolute path to directory or file to delete.
* @param bool $removeOnlyChildren set to true will only remove content inside directory.
*
* @return bool true on success; false on failure
*/
function rrmdir($source, $removeOnlyChildren = false)
{
if(empty($source) || file_exists($source) === false)
{
return false;
}
if(is_file($source) || is_link($source))
{
return unlink($source);
}
$files = new RecursiveIteratorIterator
(
new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
//$fileinfo as SplFileInfo
foreach($files as $fileinfo)
{
if($fileinfo->isDir())
{
if(rrmdir($fileinfo->getRealPath()) === false)
{
return false;
}
}
else
{
if(unlink($fileinfo->getRealPath()) === false)
{
return false;
}
}
}
if($removeOnlyChildren === false)
{
return rmdir($source);
}
return true;
}

- 610
- 1
- 7
- 17
-
1
-
@Philipp yeah I guess. Well I made a fork out of https://gist.github.com/mindplay-dk/a4aad91f5a4f1283a5e2 because I did not get it to work so I just thought I may also share it. – XzaR May 24 '15 at 09:13
-
There is a problem. It doesn't delete empty folders after all files are removed. Posting slightly modified version as an answer below. – Vladislav Rastrusny Jun 23 '15 at 16:10
-
@Vladislav Rastrusny really? It works for me. Maybe you had a folder with read-only or something. – XzaR Aug 01 '15 at 10:34
The 100% working solution
public static function rmdir_recursive($directory, $delete_parent = null)
{
$files = glob($directory . '/{,.}[!.,!..]*',GLOB_MARK|GLOB_BRACE);
foreach ($files as $file) {
if (is_dir($file)) {
self::rmdir_recursive($file, 1);
} else {
unlink($file);
}
}
if ($delete_parent) {
rmdir($directory);
}
}

- 105
- 1
- 10
-
`$directory` is used as part of the glob without any escaping, that's dangerous; also, the glob at the end won't remove anything that starts with two dots. – Fravadona Jun 27 '23 at 23:04
Example with glob() function. It will delete all files and folders recursively, including files that starts with dot.
delete_all( 'folder' );
function delete_all( $item ) {
if ( is_dir( $item ) ) {
array_map( 'delete_all', array_diff( glob( "$item/{,.}*", GLOB_BRACE ), array( "$item/.", "$item/.." ) ) );
rmdir( $item );
} else {
unlink( $item );
}
};

- 12,408
- 5
- 38
- 54
Enhanced @Artefacto 's solution - corrected typos and simplified code, working for both - empty && non-empty directories .
function recursive_rmdir($dir) {
if( is_dir($dir) ) {
$objects = array_diff( scandir($dir), array('..', '.') );
foreach ($objects as $object) {
$objectPath = $dir."/".$object;
if( is_dir($objectPath) )
recursive_rmdir($objectPath);
else
unlink($objectPath);
}
rmdir($dir);
}
}

- 13,880
- 12
- 91
- 125
It seems that all other answers assume the path given to the function is always a directory. This variant works to remove directories as well as single files:
/**
* Recursively delete a file or directory. Use with care!
*
* @param string $path
*/
function recursiveRemove(string $path) {
if (is_dir($path)) {
foreach (scandir($path) as $entry) {
if (!in_array($entry, ['.', '..'], true)) {
recursiveRemove($path . DIRECTORY_SEPARATOR . $entry);
}
}
rmdir($path);
} else {
unlink($path);
}
}
EDIT: If you're picky (and you should be picky) you may want to add code to check for scandir()
, rmdir()
and unlink()
returning an error and throw an exception if so.

- 4,349
- 40
- 45
Using DirectoryIterator and recursion correctly:
function deleteFilesThenSelf($folder) {
foreach(new DirectoryIterator($folder) as $f) {
if($f->isDot()) continue; // skip . and ..
if ($f->isFile()) {
unlink($f->getPathname());
} else if($f->isDir()) {
deleteFilesThenSelf($f->getPathname());
}
}
rmdir($folder);
}

- 89
- 3
Something like this?
function delete_folder($folder) {
$glob = glob($folder);
foreach ($glob as $g) {
if (!is_dir($g)) {
unlink($g);
} else {
delete_folder("$g/*");
rmdir($g);
}
}
}

- 11,377
- 5
- 59
- 58
-
I can't explain why but that didn't work for me. It kept trying to delete a folder that was not empty. The second answer above worked fine. – laurent Jan 10 '14 at 15:19
-
1@buggy3 Which specific code are you referring to? The link simply links to this question page. – cgogolin Apr 25 '17 at 13:58
'simple' code that works and can be read by a ten year old:
function deleteNonEmptyDir($dir)
{
if (is_dir($dir))
{
$objects = scandir($dir);
foreach ($objects as $object)
{
if ($object != "." && $object != "..")
{
if (filetype($dir . "/" . $object) == "dir")
{
deleteNonEmptyDir($dir . "/" . $object);
}
else
{
unlink($dir . "/" . $object);
}
}
}
reset($objects);
rmdir($dir);
}
}
Please note that all I did was expand/simplify and fix (didn't work for non empty dir) the solution here: In PHP how do I recursively remove all folders that aren't empty?

- 1
- 1

- 12,242
- 9
- 49
- 55
unlinkr function recursively deletes all the folders and files in given path by making sure it doesn't delete the script itself.
function unlinkr($dir, $pattern = "*") {
// find all files and folders matching pattern
$files = glob($dir . "/$pattern");
//interate thorugh the files and folders
foreach($files as $file){
//if it is a directory then re-call unlinkr function to delete files inside this directory
if (is_dir($file) and !in_array($file, array('..', '.'))) {
echo "<p>opening directory $file </p>";
unlinkr($file, $pattern);
//remove the directory itself
echo "<p> deleting directory $file </p>";
rmdir($file);
} else if(is_file($file) and ($file != __FILE__)) {
// make sure you don't delete the current script
echo "<p>deleting file $file </p>";
unlink($file);
}
}
}
if you want to delete all files and folders where you place this script then call it as following
//get current working directory
$dir = getcwd();
unlinkr($dir);
if you want to just delete just php files then call it as following
unlinkr($dir, "*.php");
you can use any other path to delete the files as well
unlinkr("/home/user/temp");
This will delete all files in home/user/temp directory.

- 2,523
- 1
- 23
- 20
I use this code ...
function rmDirectory($dir) {
foreach(glob($dir . '/*') as $file) {
if(is_dir($file))
rrmdir($file);
else
unlink($file);
}
rmdir($dir);
}
or this one...
<?php
public static function delTree($dir) {
$files = array_diff(scandir($dir), array('.','..'));
foreach ($files as $file) {
(is_dir("$dir/$file")) ? delTree("$dir/$file") : unlink("$dir/$file");
}
return rmdir($dir);
}
?>

- 51
- 1
- 5
I juste made this code, from some StackOverflow discussions. I didn't test on Linux environment yet. It is made in order to delete a file or a directory, completely :
function splRm(SplFileInfo $i)
{
$path = $i->getRealPath();
if ($i->isDir()) {
echo 'D - ' . $path . '<br />';
rmdir($path);
} elseif($i->isFile()) {
echo 'F - ' . $path . '<br />';
unlink($path);
}
}
function splRrm(SplFileInfo $j)
{
$path = $j->getRealPath();
if ($j->isDir()) {
$rdi = new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS);
$rii = new RecursiveIteratorIterator($rdi, RecursiveIteratorIterator::CHILD_FIRST);
foreach ($rii as $i) {
splRm($i);
}
}
splRm($j);
}
splRrm(new SplFileInfo(__DIR__.'/../dirOrFileName'));

- 180
- 1
- 7
function rmdir_recursive( $dirname ) {
/**
* FilesystemIterator and SKIP_DOTS
*/
if ( class_exists( 'FilesystemIterator' ) && defined( 'FilesystemIterator::SKIP_DOTS' ) ) {
if ( !is_dir( $dirname ) ) {
return false;
}
foreach( new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dirname, FilesystemIterator::SKIP_DOTS ), RecursiveIteratorIterator::CHILD_FIRST ) as $path ) {
$path->isDir() ? rmdir( $path->getPathname() ) : unlink( $path->getRealPath() );
}
return rmdir( $dirname );
}
/**
* RecursiveDirectoryIterator and SKIP_DOTS
*/
if ( class_exists( 'RecursiveDirectoryIterator' ) && defined( 'RecursiveDirectoryIterator::SKIP_DOTS' ) ) {
if ( !is_dir( $dirname ) ) {
return false;
}
foreach( new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dirname, RecursiveDirectoryIterator::SKIP_DOTS ), RecursiveIteratorIterator::CHILD_FIRST ) as $path ) {
$path->isDir() ? rmdir( $path->getPathname() ) : unlink( $path->getRealPath() );
}
return rmdir( $dirname );
}
/**
* RecursiveIteratorIterator and RecursiveDirectoryIterator
*/
if ( class_exists( 'RecursiveIteratorIterator' ) && class_exists( 'RecursiveDirectoryIterator' ) ) {
if ( !is_dir( $dirname ) ) {
return false;
}
foreach( new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dirname ), RecursiveIteratorIterator::CHILD_FIRST ) as $path ) {
if ( in_array( $path->getFilename(), array( '.', '..' ) ) ) {
continue;
}
$path->isDir() ? rmdir( $path->getPathname() ) : unlink( $path->getRealPath() );
}
return rmdir( $dirname );
}
/**
* Scandir Recursive
*/
if ( !is_dir( $dirname ) ) {
return false;
}
$objects = scandir( $dirname );
foreach ( $objects as $object ) {
if ( $object === '.' || $object === '..' ) {
continue;
}
filetype( $dirname . DIRECTORY_SEPARATOR . $object ) === 'dir' ? rmdir_recursive( $dirname . DIRECTORY_SEPARATOR . $object ) : unlink( $dirname . DIRECTORY_SEPARATOR . $object );
}
reset( $objects );
rmdir( $dirname );
return !is_dir( $dirname );
}

- 926
- 1
- 12
- 20
Modified variant of @XzaR solution. It does remove empty folders, when all files are deleted from them and it throws exceptions instead of returning false on errors.
function recursivelyRemoveDirectory($source, $removeOnlyChildren = true)
{
if (empty($source) || file_exists($source) === false) {
throw new Exception("File does not exist: '$source'");
}
if (is_file($source) || is_link($source)) {
if (false === unlink($source)) {
throw new Exception("Cannot delete file '$source'");
}
}
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($files as $fileInfo) {
/** @var SplFileInfo $fileInfo */
if ($fileInfo->isDir()) {
if ($this->recursivelyRemoveDirectory($fileInfo->getRealPath()) === false) {
throw new Exception("Failed to remove directory '{$fileInfo->getRealPath()}'");
}
if (false === rmdir($fileInfo->getRealPath())) {
throw new Exception("Failed to remove empty directory '{$fileInfo->getRealPath()}'");
}
} else {
if (unlink($fileInfo->getRealPath()) === false) {
throw new Exception("Failed to remove file '{$fileInfo->getRealPath()}'");
}
}
}
if ($removeOnlyChildren === false) {
if (false === rmdir($source)) {
throw new Exception("Cannot remove directory '$source'");
}
}
}

- 29,378
- 23
- 95
- 156
Once you finish running tests , just remove # from the #unlink and #rmdir in the class .
<?php
class RMRFiles {
function __construct(){
}
public function recScan( $mainDir, $allData = array() )
{
// hide files
$hidefiles = array(
".",
"..") ;
//start reading directory
$dirContent = scandir( $mainDir ) ;
//cycle through
foreach ( $dirContent as $key => $content )
{
$path = $mainDir . '/' . $content ;
// if is readable / file
if ( ! in_array( $content, $hidefiles ) )
{
if ( is_file( $path ) && is_readable( $path ) )
{
#delete files within directory
#unlink($path);
$allData['unlink'][] = $path ;
}
// if is readable / directory
else
if ( is_dir( $path ) && is_readable( $path ) )
{
/*recursive*/
$allData = $this->recScan( $path, $allData ) ;
#finally remove directory
$allData['rmdir'][]=$path;
#rmdir($path);
}
}
}
return $allData ;
}
}
header("Content-Type: text/plain");
/* Get absolute path of the running script
Ex : /home/user/public_html/ */
define('ABPATH', dirname(__file__) . '/');
/* The folder where we store cache files
Ex: /home/user/public_html/var/cache */
define('STOREDIR','var/cache');
$rmrf = new RMRFiles();
#here we delete folder content files & directories
print_r($rmrf->recScan(ABPATH.STOREDIR));
#finally delete scanned directory ?
#rmdir(ABPATH.STOREDIR);
?>

- 1,451
- 13
- 18
<?php
/**
* code by Nk (nk.have.a@gmail.com)
*/
class filesystem
{
public static function remove($path)
{
return is_dir($path) ? rmdir($path) : unlink($path);
}
public static function normalizePath($path)
{
return $path.(is_dir($path) && !preg_match('@/$@', $path) ? '/' : '');
}
public static function rscandir($dir, $sort = SCANDIR_SORT_ASCENDING)
{
$results = array();
if(!is_dir($dir))
return $results;
$dir = self::normalizePath($dir);
$objects = scandir($dir, $sort);
foreach($objects as $object)
if($object != '.' && $object != '..')
{
if(is_dir($dir.$object))
$results = array_merge($results, self::rscandir($dir.$object, $sort));
else
array_push($results, $dir.$object);
}
array_push($results, $dir);
return $results;
}
public static function rrmdir($dir)
{
$files = self::rscandir($dir);
foreach($files as $file)
self::remove($file);
return !file_exists($dir);
}
}
?>
cleanup.php :
<?php
/* include.. */
filesystem::rrmdir('/var/log');
filesystem::rrmdir('./cache');
?>

- 11
- 2
function deltree_cat($folder)
{
if (is_dir($folder))
{
$handle = opendir($folder);
while ($subfile = readdir($handle))
{
if ($subfile == '.' or $subfile == '..') continue;
if (is_file($subfile)) unlink("{$folder}/{$subfile}");
else deltree_cat("{$folder}/{$subfile}");
}
closedir($handle);
rmdir ($folder);
}
else
{
unlink($folder);
}
}

- 2,007
- 2
- 19
- 36
-
1If you're answering an old question that already has a number of answers including an accepted one, you need to post an explanation of what value your answer adds, not just code. Code-only answers are frowned upon in general, but especially this case. – Jared Smith Jul 14 '16 at 15:30
-
1I voted up for this answer and accepted answer. This is not bad, from my benchmark check (without `unlink`, `rmdir`) the `opendir` + `readdir` work faster that `scandir` and `RecursiveDirectoryIterator` it is also use less memory than all. To remove folder I have to `closedir` first, I was stuck at this. Thanks to this answer. – vee May 30 '17 at 13:43