324

I wonder, what's the easiest way to delete a directory with all its files in it?

I'm using rmdir(PATH . '/' . $value); to delete a folder, however, if there are files inside of it, I simply can't delete it.

1
  • Just want to note. I created multiple files and if during the process get some error, then need to delete the previously created files. When created files, forgot to use fclose($create_file); and when delete, got Warning: unlink(created_file.xml): Permission denied in.... So to avoid such errors must close created files. Commented Apr 14, 2015 at 3:47

36 Answers 36

458

There are at least two options available nowadays.

  1. Before deleting the folder, delete all its files and folders (and this means recursion!). Here is an example:

    function deleteDir(string $dirPath): void {
        if (! is_dir($dirPath)) {
            throw new InvalidArgumentException("$dirPath must be a directory");
        }
        if (substr($dirPath, strlen($dirPath) - 1, 1) != '/') {
            $dirPath .= '/';
        }
        $files = glob($dirPath . '*', GLOB_MARK);
        foreach ($files as $file) {
            if (is_dir($file)) {
                deleteDir($file);
            } else {
                unlink($file);
            }
        }
        rmdir($dirPath);
    }
    
  2. And if you are using 5.2+ you can use a RecursiveIterator to do it without implementing the recursion yourself:

    function removeDir(string $dir): void {
        $it = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
        $files = new RecursiveIteratorIterator($it,
                     RecursiveIteratorIterator::CHILD_FIRST);
        foreach($files as $file) {
            if ($file->isDir()){
                rmdir($file->getPathname());
            } else {
                unlink($file->getPathname());
            }
        }
        rmdir($dir);
    }
    
Sign up to request clarification or add additional context in comments.

12 Comments

little add-on :-) glob() does not support files like .htaccess. I used the function to clear directories made by KCFinder (CKEditor plugin) which generates both .htaccess and .thumbs (file + folder). Instead I used the scandir function to get the folder list. Just make sure you filter the '.' and '..' files from the result list.
DIRECTORY_SEPARATOR is not necessary when you're building paths to send to the os. Windows will accept forward slashes too. Its mainly useful for explode()ing a path taken from the OS. alanhogan.com/tips/php/directory-separator-not-necessary
In addition to @Alix Axel Using here the [SplFileInfo::getRealPath()] (php.net/manual/en/splfileinfo.getrealpath.php) is not a good idea. This method expands all symbolic links, that means, will be deleted a real file from somewhere instead a symlink from the target directory. You should use SplFileInfo::getPathname() instead.
I agree with @Vijit, use getPathname() instead of getRealPath(). It does the same thing without deleting more than what you are expecting to if symlinks are found.
First solution doesn't seem to delete hidden files (namely .DS_Store files in macOS), so deleting the directory fails at the end.
|
271

I generally use this to delete all files in a folder:

array_map('unlink', glob("$dirname/*.*"));

And then you can do

rmdir($dirname);

5 Comments

This doesn't delete folders recursively; it only works if the folder has only regular files in it, all of which have file extensions.
If no recursion is needed this is the best and simpliest answer so far. Thanks!
In order to remove all files from a folder, not only the ones with extensions, use glob in the following way: array_map('unlink', glob("$dirname/*")); This still doesn't allow you to delete directories nested in the folder.
Note that this will remove dot (hidden) files as well.
To delete folders recursively, just do array_map("unlink", glob("$dirname/*")); array_map("rmdir", glob("$dirname/*")); rmdir($dirname);.
113

what's the easiest way to delete a directory with all its files in it?

system("rm -rf ".escapeshellarg($dir));

15 Comments

I hope you're not serious. What happens if $dir is /
@The exactly the same as with any of the codes above. Isn't it?
Note that, depending how $dir is generated/provided, you may need to do some additional pre-processing to be safe and to avoid bugs. For example, if $dir might have an unescaped space or semi-colon in it, then there could be undesirable side effects. This is not the case with the answers that use things like rmdir() because it will handle the special characters for you.
Windows version: system('rmdir '.escapeshellarg($path).' /s /q');
@ThePixelDeveloper If your PHP script has access to delete files/folders in root directory, you have serious problems anyway.
|
56

Short function that does the job:

function deleteDir($path) {
    return is_file($path) ?
            @unlink($path) :
            array_map(__FUNCTION__, glob($path.'/*')) == @rmdir($path);
}

I use it in a Utils class like this:

class Utils {
    public static function deleteDir($path) {
        $class_func = array(__CLASS__, __FUNCTION__);
        return is_file($path) ?
                @unlink($path) :
                array_map($class_func, glob($path.'/*')) == @rmdir($path);
    }
}

With great power comes great responsibility: When you call this function with an empty value, it will delete files starting in root (/). As a safeguard you can check if path is empty:

function deleteDir($path) {
    if (empty($path)) { 
        return false;
    }
    return is_file($path) ?
            @unlink($path) :
            array_map(__FUNCTION__, glob($path.'/*')) == @rmdir($path);
}

7 Comments

The static one doesn't work because $this === NULL when you call a static function on a class. It would work if $this_func = array(__CLASS__, __FUNCTION__);
Can someone explain the line array_map($class_func, glob($path.'/*')) == @rmdir($path)? I guess he's recursing through the subfolders, but what does the == @rmdir part do? How does the <array of booleans> == <boolean> return the answer? Does it check if each return value of the recursion is the same as the boolean on the right?
It's a trick to merge two statements into one statement. This is because ternary operators allow only one statement per argument. array_map(...) removes all files within the directory, @rmdir(...) removes the directory itself.
Be careful! This function does not check if the path really exists. If you pass an empty argument, the function will start to delete files starting from the root! Add a sanity check to your path before you run this function.
Some people did not see Tatu's comment and recursively deleted /, so I appended a safeguarded version to my post.
|
42

As seen in most voted comment on PHP manual page about rmdir() (see http://php.net/manual/es/function.rmdir.php), glob() function does not return hidden files. scandir() is provided as an alternative that solves that issue.

Algorithm described there (which worked like a charm in my case) is:

<?php 
    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); 
    } 
?>

4 Comments

can you please explain is_dir("$dir/$file") - did not meet with the "$dir/$file" parameter
What do you mean? It checks if the entry found in a directory ($file) is a directory or a file. "$dir/$file" is the same as $dir . "/" . $file.
I honestly did not know you can concatenate variables like this :) thx
The only challenge I came across with this example is that it doesn't handle symlinks to directories vary well within the directory. To do this, change the is_dir check to (is_dir("$dir/$file") && !is_link("$dir/$file"))
25

You may use Symfony's Filesystem (code):

// composer require symfony/filesystem

use Symfony\Component\Filesystem\Filesystem;

(new Filesystem)->remove($dir);

However I couldn't delete some complex directory structures with this method, so first you should try it to ensure it's working properly.


I could delete the said directory structure using a Windows specific implementation:

$dir = strtr($dir, '/', '\\');
// quotes are important, otherwise one could
// delete "foo" instead of "foo bar"
system('RMDIR /S /Q "'.$dir.'"');


And just for the sake of completeness, here is an old code of mine:

function xrmdir($dir) {
    $items = scandir($dir);
    foreach ($items as $item) {
        if ($item === '.' || $item === '..') {
            continue;
        }
        $path = $dir.'/'.$item;
        if (is_dir($path)) {
            xrmdir($path);
        } else {
            unlink($path);
        }
    }
    rmdir($dir);
}

Comments

19

This is a shorter Version works great to me

function deleteDirectory($dirPath) {
    if (is_dir($dirPath)) {
        $objects = scandir($dirPath);
        foreach ($objects as $object) {
            if ($object != "." && $object !="..") {
                if (filetype($dirPath . DIRECTORY_SEPARATOR . $object) == "dir") {
                    deleteDirectory($dirPath . DIRECTORY_SEPARATOR . $object);
                } else {
                    unlink($dirPath . DIRECTORY_SEPARATOR . $object);
                }
            }
        }
    reset($objects);
    rmdir($dirPath);
    }
}

Comments

15

You can try as follows:

/*
 * Remove the directory and its content (all files and subdirectories).
 * @param string $dir the directory name
 */
function rmrf($dir) {
    foreach (glob($dir) as $file) {
        if (is_dir($file)) { 
            rmrf("$file/*");
            rmdir($file);
        } else {
            unlink($file);
        }
    }
}

1 Comment

The simplicity of the function is beautiful and it works excellently, except on dot files.
14

I can't believe there are 30+ answers for this. Recursively deleting a folder in PHP could take minutes depending on the depth of the directory and the number of files in it! You can do this with one line of code ...

shell_exec("rm -rf " . $dir);

If you're concerned with deleting the entire filesystem, make sure your $dir path is exactly what you want first. NEVER allow a user to input something that can directly delete files without first heavily validating the input. That's essential coding practice.

4 Comments

I am doing clean up on the /tmp folder so shell_exec("rm -rf " . sys_get_temp_dir() . "/{$dir}"); is easy, straight forward and lighting fast.
This! but I would do shell_exec("rm -rf '$dir'");
I'm using this with a cron job to remove uploaded security camera images from the previous day. Obviously as stated above don't let users have access to this and make sure you got your $dir correct!
Not everyone has access to shell_exec(), but if you do then this would be the way to go.
13

This one works for me:

function removeDirectory($path) {
    $files = glob($path . '/*');
    foreach ($files as $file) {
        is_dir($file) ? removeDirectory($file) : unlink($file);
    }
    rmdir($path);
    return;
}

Comments

10

Here you have one nice and simple recursion for deleting all files in source directory including that directory:

function delete_dir($src) { 
    $dir = opendir($src);
    while(false !== ( $file = readdir($dir)) ) { 
        if (( $file != '.' ) && ( $file != '..' )) { 
            if ( is_dir($src . '/' . $file) ) { 
                delete_dir($src . '/' . $file); 
            } 
            else { 
                unlink($src . '/' . $file); 
            } 
        } 
    } 
    closedir($dir); 
    rmdir($src);

}

Function is based on recursion made for copying directory. You can find that function here: Copy entire contents of a directory to another using php

Comments

8

The Best Solution for me

my_folder_delete("../path/folder");

code:

function my_folder_delete($path) {
    if (in_array($path, ['.', '/'])) return; // ensure to avoid accidents
    if(!empty($path) && is_dir($path) ){
        $dir  = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS); //upper dirs are not included,otherwise DISASTER HAPPENS :)
        $files = new RecursiveIteratorIterator($dir, RecursiveIteratorIterator::CHILD_FIRST);
        foreach ($files as $f) {if (is_file($f)) {unlink($f);} else {$empty_dirs[] = $f;} } if (!empty($empty_dirs)) {foreach ($empty_dirs as $eachDir) {rmdir($eachDir);}} rmdir($path);
    }
}

p.s. REMEMBER!
dont pass EMPTY VALUES to any Directory deleting functions!!! (backup them always, otherwise one day you might get DISASTER!!)

Comments

5

Litle bit modify of alcuadrado's code - glob don't see files with name from points like .htaccess so I use scandir and script deletes itself - check __FILE__.

function deleteDir($dirPath) {
    if (!is_dir($dirPath)) {
        throw new InvalidArgumentException("$dirPath must be a directory");
    }
    if (substr($dirPath, strlen($dirPath) - 1, 1) != '/') {
        $dirPath .= '/';
    }
    $files = scandir($dirPath); 
    foreach ($files as $file) {
        if ($file === '.' || $file === '..') continue;
        if (is_dir($dirPath.$file)) {
            deleteDir($dirPath.$file);
        } else {
            if ($dirPath.$file !== __FILE__) {
                unlink($dirPath.$file);
            }
        }
    }
    rmdir($dirPath);
}

Comments

5

Glob function doesn't return the hidden files, therefore scandir can be more useful, when trying to delete recursively a tree.

<?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);
  }
?>

Comments

5

Example for the Linux server: exec('rm -f -r ' . $cache_folder . '/*');

1 Comment

I usually like to add a sanity check on $cache_folder before running rm -rf to avoid costly mistakes
4

What about this:

function recursiveDelete($dirPath, $deleteParent = true){
    foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dirPath, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST) as $path) {
        $path->isFile() ? unlink($path->getPathname()) : rmdir($path->getPathname());
    }
    if($deleteParent) rmdir($dirPath);
}

Comments

4

I want to expand on the answer by @alcuadrado with the comment by @Vijit for handling symlinks. Firstly, use getRealPath(). But then, if you have any symlinks that are folders it will fail as it will try and call rmdir on a link - so you need an extra check.

$it = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
$files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
foreach($files as $file) {
    if ($file->isLink()) {
        unlink($file->getPathname());
    } else if ($file->isDir()){
        rmdir($file->getPathname());
    } else {
        unlink($file->getPathname());
    }
}
rmdir($dir);

1 Comment

I don't have enough rep to comment on the answer directly.
3

I prefer this because it still returns TRUE when it succeeds and FALSE when it fails, and it also prevents a bug where an empty path might try and delete everything from '/*' !!:

function deleteDir($path)
{
    return !empty($path) && is_file($path) ?
        @unlink($path) :
        (array_reduce(glob($path.'/*'), function ($r, $i) { return $r && deleteDir($i); }, TRUE)) && @rmdir($path);
}

Comments

3

Using DirectoryIterator an equivalent of a previous answer…

function deleteFolder($rootPath)
{   
    foreach(new DirectoryIterator($rootPath) as $fileToDelete)
    {
        if($fileToDelete->isDot()) continue;
        if ($fileToDelete->isFile())
            unlink($fileToDelete->getPathName());
        if ($fileToDelete->isDir())
            deleteFolder($fileToDelete->getPathName());
    }

    rmdir($rootPath);
}

Comments

3

you can try this simple 12 line of code for delete folder or folder files... happy coding... ;) :)

function deleteAll($str) {
    if (is_file($str)) {
        return unlink($str);
    }
    elseif (is_dir($str)) {
        $scan = glob(rtrim($str,'/').'/*');
        foreach($scan as $index=>$path) {
            $this->deleteAll($path);
        }            
        return @rmdir($str);
    }
}

Comments

2

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);
        }
    }
}

Comments

1

Delete all files in Folder
array_map('unlink', glob("$directory/*.*"));
Delete all .*-Files in Folder (without: "." and "..")
array_map('unlink', array_diff(glob("$directory/.*),array("$directory/.","$directory/..")))
Now delete the Folder itself
rmdir($directory)

Comments

1

Simple and Easy...

$dir ='pathtodir';
if (is_dir($dir)) {
  foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)) as $filename) {
    if ($filename->isDir()) continue;
    unlink($filename);
  }
  rmdir($dir);
}

Comments

1

2 cents to add to THIS answer above, which is great BTW

After your glob (or similar) function has scanned/read the directory, add a conditional to check the response is not empty, or an invalid argument supplied for foreach() warning will be thrown. So...

if( ! empty( $files ) )
{
    foreach( $files as $file )
    {
        // do your stuff here...
    }
}

My full function (as an object method):

    private function recursiveRemoveDirectory( $directory )
    {
        if( ! is_dir( $directory ) )
        {
            throw new InvalidArgumentException( "$directory must be a directory" );
        }

        if( substr( $directory, strlen( $directory ) - 1, 1 ) != '/' )
        {
            $directory .= '/';
        }

        $files = glob( $directory . "*" );

        if( ! empty( $files ) )
        {
            foreach( $files as $file )
            {
                if( is_dir( $file ) )
                {
                    $this->recursiveRemoveDirectory( $file );
                }
                else
                {
                    unlink( $file );
                }
            }               
        }
        rmdir( $directory );

    } // END recursiveRemoveDirectory()

Comments

1

Here is the solution that works perfect:

function unlink_r($from) {
    if (!file_exists($from)) {return false;}
    $dir = opendir($from);
    while (false !== ($file = readdir($dir))) {
        if ($file == '.' OR $file == '..') {continue;}

        if (is_dir($from . DIRECTORY_SEPARATOR . $file)) {
            unlink_r($from . DIRECTORY_SEPARATOR . $file);
        }
        else {
            unlink($from . DIRECTORY_SEPARATOR . $file);
        }
    }
    rmdir($from);
    closedir($dir);
    return true;
}

Comments

1

What about this?

function Delete_Directory($Dir) 
{
  if(is_dir($Dir))
  {
      $files = glob( $Dir . '*', GLOB_MARK ); //GLOB_MARK adds a slash to directories returned

      foreach( $files as $file )
      {
          Delete_Directory( $file );      
      }
      if(file_exists($Dir))
      {
          rmdir($Dir);
      }
  } 
  elseif(is_file($Dir)) 
  {
     unlink( $Dir );  
  }
}

Refrence: https://paulund.co.uk/php-delete-directory-and-files-in-directory

Comments

1

You could copy the YII helpers

$directory (string) - to be deleted recursively.

$options (array) - for the directory removal. Valid options are: traverseSymlinks: boolean, whether symlinks to the directories should be traversed too. Defaults to false, meaning that the content of the symlinked directory would not be deleted. Only symlink would be removed in that default case.

public static function removeDirectory($directory,$options=array())
{
    if(!isset($options['traverseSymlinks']))
        $options['traverseSymlinks']=false;
    $items=glob($directory.DIRECTORY_SEPARATOR.'{,.}*',GLOB_MARK | GLOB_BRACE);
    foreach($items as $item)
    {
        if(basename($item)=='.' || basename($item)=='..')
            continue;
        if(substr($item,-1)==DIRECTORY_SEPARATOR)
        {
            if(!$options['traverseSymlinks'] && is_link(rtrim($item,DIRECTORY_SEPARATOR)))
                unlink(rtrim($item,DIRECTORY_SEPARATOR));
            else
                self::removeDirectory($item,$options);
        }
        else
            unlink($item);
    }
    if(is_dir($directory=rtrim($directory,'\\/')))
    {
        if(is_link($directory))
            unlink($directory);
        else
            rmdir($directory);
    }
}

Comments

0
<?php
  function rrmdir($dir) {
  if (is_dir($dir)) {
    $objects = scandir($dir);
    foreach ($objects as $object) {
      if ($object != "." && $object != "..") {
        if (filetype($dir."/".$object) == "dir") 
           rrmdir($dir."/".$object); 
        else unlink   ($dir."/".$object);
      }
    }
    reset($objects);
    rmdir($dir);
  }
 }
?>

Have your tryed out the obove code from php.net

Work for me fine

Comments

0

For windows:

system("rmdir ".escapeshellarg($path) . " /s /q");

Comments

0

Like Playnox's solution, but with the elegant built-in DirectoryIterator:

function delete_directory($dirPath){
 if(is_dir($dirPath)){
  $objects=new DirectoryIterator($dirPath);
   foreach ($objects as $object){
    if(!$object->isDot()){
     if($object->isDir()){
      delete_directory($object->getPathname());
     }else{
      unlink($object->getPathname());
     }
    }
   }
   rmdir($dirPath);
  }else{
   throw new Exception(__FUNCTION__.'(dirPath): dirPath is not a directory!');
  }
 }

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.