Recursively deleting a folder in PHP
Posted on April 5th, 2004 in Code Repository | 102 Comments »
PHP’s rmdir function does not allow the deletion of a folder if it is not empty. The rmdirr function below allows the removal of a folder and all its contents recursively, as would rm -rf on linux.
/**
* Delete a file, or a folder and its contents (recursive algorithm)
*
* @author Aidan Lister <aidan@php.net>
* @version 1.0.3
* @link http://aidanlister.com/2004/04/recursively-deleting-a-folder-in-php/
* @param string $dirname Directory to delete
* @return bool Returns TRUE on success, FALSE on failure
*/
function rmdirr($dirname)
{
// Sanity check
if (!file_exists($dirname)) {
return false;
}
// Simple delete for a file
if (is_file($dirname) || is_link($dirname)) {
return unlink($dirname);
}
// Loop through the folder
$dir = dir($dirname);
while (false !== $entry = $dir->read()) {
// Skip pointers
if ($entry == '.' || $entry == '..') {
continue;
}
// Recurse
rmdirr($dirname . DIRECTORY_SEPARATOR . $entry);
}
// Clean up
$dir->close();
return rmdir($dirname);
}
All good code should include tests, so here we go:
// Create a directory and file tree
mkdir('testdelete');
mkdir('testdelete/one-a');
touch('testdelete/one-a/testfile');
mkdir('testdelete/one-b');
// Add some hidden files for good measure
touch('testdelete/one-b/.hiddenfile');
mkdir('testdelete/one-c');
touch('testdelete/one-c/.hiddenfile');
// Add some more depth
mkdir('testdelete/one-c/two-a');
touch('testdelete/one-c/two-a/testfile');
mkdir('testdelete/one-d/');
// Test that symlinks are not followed
mkdir('testlink');
touch('testlink/testfile');
symlink(getcwd() . '/testlink/testfile', 'testdelete/one-d/my-symlink');
symlink(getcwd() . '/testlink', 'testdelete/one-d/my-symlink-dir');
// Run the actual delete
$status = rmdirr('testdelete');
// Check if we passed the test
if ($status === true &&
!file_exists('testdelete') &&
file_exists('testlink/testfile')) {
echo 'TEST PASSED';
rmdirr('testlink');
} else {
echo 'TEST FAILED';
}
This simple testing script should yield a “TEST PASSED” message when run from your browser or command line.
Although I worry that the paradigm-of-choice may overwhelm some readers, in the interests of completeness I have also included a stack based (instead of recursion based) version of rmdirr below:
/**
* Delete a file, or a folder and its contents (stack algorithm)
*
* @author Aidan Lister <aidan@php.net>
* @version 1.0.0
* @link http://aidanlister.com/repos/v/function.rmdirr.php
* @param string $dirname Directory to delete
* @return bool Returns TRUE on success, FALSE on failure
*/
function rmdirr($dirname)
{
// Sanity check
if (!file_exists($dirname)) {
return false;
}
// Simple delete for a file
if (is_file($dirname) || is_link($dirname)) {
return unlink($dirname);
}
// Create and iterate stack
$stack = array($dirname);
while ($entry = array_pop($stack)) {
// Watch for symlinks
if (is_link($entry)) {
unlink($entry);
continue;
}
// Attempt to remove the directory
if (@rmdir($entry)) {
continue;
}
// Otherwise add it to the stack
$stack[] = $entry;
$dh = opendir($entry);
while (false !== $child = readdir($dh)) {
// Ignore pointers
if ($child === '.' || $child === '..') {
continue;
}
// Unlink files and add directories to stack
$child = $entry . DIRECTORY_SEPARATOR . $child;
if (is_dir($child) && !is_link($child)) {
$stack[] = $child;
} else {
unlink($child);
}
}
closedir($dh);
print_r($stack);
}
return true;
}
Rather than the function calling itself once for each directory, this function maintains an internal stack. Because this function uses the error suppression operator it incurs a speed penalty; however, it will work for very deep directory structures.
The recursive method can cause PHP to segfault when PHP’s own internal stack grows too large (stack overflow), but this is only a problem if you have directories nested 1000+ layers deep.
Summary: use the recursive method (listed first) unless you really don’t like recursion, or have a very deep tree structure. Happy deleting!
102 Responses
Very useful script. Thanks!
Thanx..
nice! Ty.
nice
thanks man
Just what I needed, Thanx
good work, thank a lot
thanks man for the script.
thanks for saving me the time of rewriting that
this script is very very useful.. just wat i need…
Thanks a lot!
great post
Man, that script is a must!
Thanks a lot
Nice! Thanks, this was what I needed!
thank you!!!
Thanks, very useful script!
Very nice.
Thanks for such good script!
Very interesting. I wrote similar, but have several problems, such as: script shows me error that access is denied (when it must delete directory(rmdir), but permission was 0777).
Had the same “permission denied”-problem when I tried to delete a folder just after it was closed by “dir->close();”. But it works if you call that function twice ( i.e. “dir->close(); dir->close();”) (php version 4.2.1, Win XP, IIS)
[Editor's Note: WinXP has some file locking issues, especially on slower machines. If you add a usleep(10) above the unlink() function, it should resolve this (but also slow down your script). If you're not using a slow computer, don't worry about it.]
thank a lot too!!
Thank ‘s a lot
Before i used your script i’d having an error message : permission denied…
Now with your function yahoooo it runs like i want
: )
AWESOME. Thanks.
Thanks for your help… Now I will have less code to write…
I added this before the first “unlink” to avoid parse errors:
if (!file_exists($dirname)) return false;
[Editor's Note: Good point, I added this in now]
Hey – just what I needed with a sleepy can’t be bothered brain.
Thanks very much |-)
very useful script. thanks
yes, this helps a lot… unlink should take wildcards and be recursive if desired, but the dangers of it are obvious… this routine does the job well
Perfect, both this script and the mkdir script. Unbelievable, exactly what i needed. Thank you
Very nice – I never knew about $dir before.
Anyhow, I wrote my own function similar (not using $dir) and it’s virtually the same as yours except I noticed that (when making mine), the entire “Delete deep directories” thing is unnecessary – all you need to do is call rmdirr() there – no is_dir test is required.
The reason for this is the first thing rmdirr() does is test for a plain file and unlink it, so why put 2 unlinks when all you need is 1 – save some lines of code, just recurse the rmdirr() function and have the 2nd call to it unlink the file.
[Editor's Note: Ah, I overlooked that, I've updated the code]
Good job for the programmer
I had to delete around 25,000 directories and 100,000 files from a site. The FTP program was going to take several days. With this, modifying the code, testing and deleting was less than an hour. Thanks a lot.
this is a super job!
thanks!!!
thanks~!!!!!!!!
Thank you very much! Your work is wonderfull.
Thanks a lot:-)
Not to be a wise guy, but…. How do you use this??
I have directories that contain images and it would be sweet if they can be vaporized at the same time that I delete the users from the db who uploaded the images in the first place
(reading the comments this seems useful but I need HELLLP!)
[Editor’s Note: You can use this the same way you’d use any other function…
<?php
require_once ‘function.rmdirr.php’;
rmdirr(‘/path/to/folder’);
?>
Thx for your function, very good!
I bookmarked your site..very nice function and classes..
With this script I had the “Permission-denied” problems. I’d try to add umask function to resolve:
<?php
// Clean up
$dir->close();
umask (0755);
return rmdir($dirname);
?>
Thanks a lot for your script ! Very usefull.
thankyou v muchly !
It`s a fantastic php`s script!
Thank !
thank you man , good work
Thank
juste great … thx
basic and useful
why the heck isn’t this built in to PHP? They have 10 sort functions but no ‘rm -rf’ function.
Cool! Thx
Thanks!
thanks very much, just what i needed
First of all, great function. I’ve got a small problem going on though. Lets say I have a directory with two subdirectories. If I am browsing the main directory with Windows Explorer, and run the function it will go perfectly. Now if I am browsing one of the subdirectories and run the script, it will empty everything out from the main directory but does not delete it alleging that it is not empty.
Cheers, great job!
[Editor's Note: This is windows again. It locks a directory when you are viewing it with windows explorer, it's a pain in the butt.]
muito bom esse script, salvou minha vida!!!
Thank you for this script! It really helped me!
Just look out for symlinks in linux/unix – tis script will follow symlinks to another folder and start emptying that too.
To avoid this change:
if (is_file($dirname)) {
to:
if (is_file($dirname) || is_link($dirname)) {
[Editor's Note: Ouch, that could be nasty. I've fixed this, Thanks.]
thanx a lot man…
Thanx. This script helps me a LOT.
its simply suberb……….
thankx, keep it up
Very helpful, saved me a lot of grief when I was haveing 550: permission denied error’s.
U SAVE MY LIFEEEE!!!!
thanks
Great script. Just what I was looking for!
Thanks
Thank you,
very useful and simply!
Everytime useful man u saved my life i coded with the most of functions my site lol size_readable dir_size and shit like that verry nice to make something like this
Tanks!
good
Thank you
thanks, good work
nice work, cheers matey, just flipped over to the php world from jsp. It seems php is supported more than jsp by the web hosts out there, so I had to do it.
Sorry asp, didn’t really give you a thought
thanks
Nice script. was looking for something like this for a few days now.
Thx
good work !
– excellent —
WAOW! Worked perfectly first time. Genius.
*king Excellent
Muchas gracias por el script me ayudo mucho
finaly a good rmdir function, thx!
Its just excellent.
Perfect, just what I needed, thanks.
QUALITY. THANKS!
Simply Beautiful
Great script. Had an install go crazy leaving a directory owned by the webserver. I was pulling my hair out. Thanks.
Hey its a very good script. Thanks
Thanks! Keep up the great work, Aidan.
gracias Aidan!!
I use your function in my PHP module:
http://www.viciao2k3.net/modules.php?name=ftp2nuke
thx a lot !!
Works like a charm,
thanks
Excellent. I was just about to write something similar myself and found your link on the PHP website. Great job, thanks.
Thank you very much! This should be a standard function within PHP. Very useful. Works without any glich.
Rocking Man…………………..
Great Job
Thank you
Instead of recursing through the directory tree, you can try
passthru(‘rm -rf directory_name’);
Gracias! Me fue muy Ăștil.
Very helpful. Thanks.
Excellent, thanks a lot.
The recursive one is wrong because it doesn’t check the return value of the recursive calls.
Chris, It’s not wrong, it’s part of the design. If you want the function to give up the first time it meets a file it can’t delete, you can check the return value. Otherwise, similar to how most filesystems behave, we keep going and let the user know we missed a file after we’re done.
Thanks, helps me a lot
btw: the URL you’ve written on the script an here is no longer correct
Thank you for scripts
Its just excellent.
Wow thanks alot! I couldnt delete some files using FTP so I tried cPanel File Manager but still didnt work.. When I used your script it deleted them in less than 5 seconds..!
excellent bro!! thx a lot..
Thanks! Keep up the good work, Aidan.
Didn’t use it as a function, but it still worked, thanks
Wonderful, Excellent script. Thank you soo much
NICE!!! GOOD JOB!!!
I get pleasure from, cause I discovered just what I used to be having a look for. You have ended my four day long hunt! God Bless you man. Have a great day. Bye