How do I delete a directory and its entire contents (files and subdirectories) in PHP?
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);
}
}
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. –
Gignac Recursion is generally bad practice in php
as it doesn't support tail call optimization. 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. –
Hipbone 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).
SKIP_DOTS
was only introduced in PHP 5.3? Where did you saw that? –
Respire getPathname()
method instead of getRealPath()
? –
Respire rmdir($dir)
at the end of the script. –
Peder rmdir()
and unlink()
, e.g. aborts with E_WARNING
and returns true
or false
indicating success. –
Gutbucket $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST );
–
Georgeannageorgeanne FilesystemIterator
is not a recursive iterator. –
Var 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);
}
rm -rf /
== recurseRmdir('/')
:) –
Emancipation is_dir("$dir/$file")
to is_dir("$dir/$file") && !is_link("$dir/$file")
–
Tumer For *nix you can use a shell_exec
for rm -R
or DEL /S folder_name
for Windows.
DEL /S folder_name
for Windows –
Feeler RMDIR /S /Q folder_name
is what worked for me –
Lorianne exec('rm -rf ' . __DIR__ . '/output/*.log');
–
Suggestible If you are using Yii then you can leave it to the framework:
CFileHelper::removeDirectory($my_directory);
<?php
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use SplFileInfo;
# https://mcmap.net/q/149365/-how-do-i-recursively-delete-a-directory-and-its-entire-contents-files-sub-dirs-in-php-duplicate
# 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;
}
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);
}
}
$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. –
Carboxylase 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 );
}
};
system('rm -fr folder')
–
Pettway 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);
}
}
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.
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);
}
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);
}
}
}
'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?
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.
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);
}
?>
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'));
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 );
}
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'");
}
}
}
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);
?>
<?php
/**
* code by Nk ([email protected])
*/
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');
?>
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);
}
}
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. –
Simonnesimonpure © 2022 - 2024 — McMap. All rights reserved.