¿Cómo elimino recursivamente un directorio y todo su contenido (archivos + subdirectorios) en PHP?

Respuestas:

207

La sección aportada por el usuario en la página del manual de rmdircontiene una implementación decente:

 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); 
   } 
 }
Artefacto
fuente
1
@The Pixel Developer: agregué una respuesta que lo muestra.
salathe
2
echa un vistazo a la solución que alguien me dio para la misma pregunta: parece que glob funciona mejor: stackoverflow.com/questions/11267086/…
NoodleOfDeath
Esto llama is_dirdos veces para cada directorio recursivo. Si el argumento es un enlace simbólico, también lo sigue en lugar de eliminar el enlace simbólico, que puede o no ser lo que desea. En cualquier caso, no es lo que rm -rfhace.
Vladimir Panteleev
116

Sobre la base del comentario de The Pixel Developer , un fragmento que use el SPL podría verse así:

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

Nota: No hace ninguna comprobación de cordura y hace uso de la bandera SKIP_DOTS introducido con el FilesystemIterator en PHP 5.3.0. Por supuesto, el $todopodría ser un if/ else. El punto importante es que CHILD_FIRSTse usa para iterar sobre los hijos (archivos) primero antes de sus padres (carpetas).

salathe
fuente
SKIP_DOTSsolo se introdujo en PHP 5.3? ¿Dónde viste eso?
Alix Axel
Gracias. Además: ¿no deberías usar el getPathname()método en lugar de getRealPath()?
Alix Axel
3
Esta solución funciona bien, sin embargo, elimina todo ... excepto el directorio (ya sea vacío o no). Debe haber un rmdir($dir)al final del guión.
Laurent
3
Aquí está la misma función sin envolver, bloqueada por documentos y hecha coherente con rmdir()y unlink(), por ejemplo, aborta con E_WARNINGy devuelve trueo falseindica éxito.
mindplay.dk
2
@dbf no, no lo será, FilesystemIteratorno es un iterador recursivo.
salathe
17

Elimina todos los archivos y carpetas en la ruta.

function recurseRmdir($dir) {
  $files = array_diff(scandir($dir), array('.','..'));
  foreach ($files as $file) {
    (is_dir("$dir/$file")) ? recurseRmdir("$dir/$file") : unlink("$dir/$file");
  }
  return rmdir($dir);
}
Liko
fuente
1
rm -rf /== recurseRmdir('/'):)
Aaron Esau
55
¡Tenga en cuenta que esto no es seguro para enlaces simbólicos! Necesita una comprobación de cordura después de is_dir para verificar también que es! Is_link, porque de lo contrario puede vincular a una carpeta externa que luego se elimina y esto puede considerarse un agujero de seguridad. Por lo que debe cambiar is_dir("$dir/$file")ais_dir("$dir/$file") && !is_link("$dir/$file")
Kira M. Backes
13

Para * nix puede usar un shell_execpara rm -Ro DEL /S folder_namepara Windows.

ankitjaininfo
fuente
2
¿qué tal DEL /S folder_namepara Windows
ankitjaininfo
@ Gordon RMDIR /S /Q folder_namees lo que funcionó para mí
Brian Leishman,
2
@ WiR3D siempre y cuando el comando exec no contenga información del usuario, debería ser bueno. Ej:exec('rm -rf ' . __DIR__ . '/output/*.log');
Brian Hannay el
5
<?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;
}
XzaR
fuente
Sugerencia bastante compleja ;-)
Philipp
@Philipp, sí, supongo. Bueno, hice una bifurcación de gist.github.com/mindplay-dk/a4aad91f5a4f1283a5e2 porque no pude hacerlo funcionar, así que pensé que también podría compartirlo.
XzaR
Hay un problema. No elimina las carpetas vacías después de eliminar todos los archivos. Publicando una versión ligeramente modificada como respuesta a continuación.
Vladislav Rastrusny
@Vladislav Rastrusny realmente? Esto funciona para mi. Tal vez tenías una carpeta con solo lectura o algo así.
XzaR
1

Código 'simple' que funciona y puede ser leído por un niño de diez años:

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

Tenga en cuenta que todo lo que hice fue expandir / simplificar y corregir (no funcionó para un directorio no vacío) la solución aquí: en PHP, ¿cómo elimino recursivamente todas las carpetas que no están vacías?

Programador
fuente
1

Solución mejorada de @Artefacto - errores tipográficos corregidos y código simplificado, trabajando para ambos - directorios vacíos y no vacíos.

  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); 
    } 
  }
jave.web
fuente
1

La solución 100% funcional

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);
    }
  }
David Pankov
fuente
0

¿Algo como esto?

function delete_folder($folder) {
    $glob = glob($folder);
    foreach ($glob as $g) {
        if (!is_dir($g)) {
            unlink($g);
        } else {
            delete_folder("$g/*");
            rmdir($g);
        }
    }
}
K-Gun
fuente
No puedo explicar por qué, pero eso no funcionó para mí. Seguía intentando eliminar una carpeta que no estaba vacía. La segunda respuesta anterior funcionó bien.
Laurent
1
@ buggy3 ¿A qué código específico te refieres? El enlace simplemente enlaza con esta página de preguntas.
cgogolin
0

Ejemplo con la función glob () . Eliminará todos los archivos y carpetas de forma recursiva, incluidos los archivos que comienzan con punto.

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 );
    }
};
Danijel
fuente
Fui consystem('rm -fr folder')
Itay Moav -Malimovka
0

la función unlinkr elimina de forma recursiva todas las carpetas y archivos en la ruta dada al asegurarse de que no elimine el script en sí.

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

si desea eliminar todos los archivos y carpetas donde coloca este script, llámelo de la siguiente manera

//get current working directory
$dir = getcwd();
unlinkr($dir);

si solo desea eliminar solo archivos php, llámelo de la siguiente manera

unlinkr($dir, "*.php");

puedes usar cualquier otra ruta para eliminar los archivos también

unlinkr("/home/user/temp");

Esto eliminará todos los archivos en el directorio home / user / temp.

Tofeeq
fuente
0

Yo uso este código ...

 function rmDirectory($dir) {
        foreach(glob($dir . '/*') as $file) {
            if(is_dir($file))
                rrmdir($file);
            else
                unlink($file);
        }
        rmdir($dir);
    }

o este...

<?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); 
  } 
?>
Hamed
fuente
¿Es esto recursivo?
Martin Tournoij
0

Una vez que termine de ejecutar las pruebas, simplemente elimine # del #unlink y #rmdir en la clase.

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

?>
Alin Razvan
fuente
0
<?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');

?>
Nkc
fuente
0

Parece que todas las demás respuestas suponen que la ruta dada a la función es siempre un directorio. Esta variante funciona para eliminar directorios y archivos individuales:

/**
 * Recursively delete a file or directory.  Use with care!
 *
 * @param string $path
 */
function recursiveRemove($path) {
    if (is_dir($path)) {
        foreach (scandir($path) as $entry) {
            if (!in_array($entry, ['.', '..'])) {
                recursiveRemove($path . DIRECTORY_SEPARATOR . $entry);
            }
        }
        rmdir($path);
    } else {
        unlink($path);
    }
}
jlh
fuente
0

Usando DirectoryIterator y recursión correctamente:

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);
}
AvaLanCS
fuente
-1

Acabo de hacer este código, de algunas discusiones de StackOverflow. Todavía no he probado en el entorno Linux. Está hecho para eliminar un archivo o un directorio, completamente:

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'));
Chicna
fuente
-1
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 );

}
D3F4ULT
fuente
-1

Variante modificada de la solución @XzaR. Elimina las carpetas vacías, cuando todos los archivos se eliminan de ellas y arroja excepciones en lugar de devolver errores falsos.

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'");
        }
    }
}
Vladislav Rastrusny
fuente
-1
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);
     }
}
Ilya Yaremchuk
fuente
1
Si está respondiendo una pregunta anterior que ya tiene varias respuestas, incluida una aceptada, debe publicar una explicación del valor que agrega su respuesta, no solo el código. Las respuestas de solo código están mal vistas en general, pero especialmente en este caso.
Jared Smith
He votado a favor de esta respuesta y acepté la respuesta. Esto no está mal, desde mi prueba de referencia (sin ) unlink, rmdirel opendir+ readdirfunciona más rápido scandiry RecursiveDirectoryIteratortambién usa menos memoria que todos. Para eliminar la carpeta closedir, primero tengo que estar atrapado en esto. Gracias a esta respuesta.
vee