Comment supprimer récursivement un répertoire et tout son contenu (fichiers + sous-répertoires) en PHP?


Réponses:


207

La section fournie par l'utilisateur dans la page de manuel de rmdircontient une implémentation décente:

 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 - J'ai ajouté une réponse montrant cela.
salathe le

2
Découvrez la solution que quelqu'un m'a donnée pour la même question: glob semble fonctionner mieux: stackoverflow.com/questions/11267086/…
NoodleOfDeath

Cela appelle is_dirdeux fois pour chaque répertoire récursé. Si l'argument est un lien symbolique, il le suit également au lieu de supprimer le lien symbolique, ce qui peut ou non être ce que vous voulez. En tout cas, ce n'est pas ce qui rm -rffait.
Vladimir Panteleev le

116

En s'appuyant sur le commentaire du Pixel Developer , un extrait de code utilisant le SPL pourrait ressembler à:

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

Remarque: il ne fait aucune vérification de cohérence et utilise l'indicateur SKIP_DOTS introduit avec le FilesystemIterator dans PHP 5.3.0. Bien sûr, le $todopourrait être un if/ else. Le point important est qu'il CHILD_FIRSTest utilisé pour parcourir les enfants (fichiers) avant leur parent (dossiers).


SKIP_DOTSn'a été introduit qu'en PHP 5.3? Où avez-vous vu ça?
Alix Axel

Je vous remercie. Aussi: ne devriez-vous pas utiliser la getPathname()méthode au lieu de getRealPath()?
Alix Axel

3
Cette solution fonctionne bien, mais elle supprime tout ... sauf le répertoire (vide ou non). Il devrait y avoir un rmdir($dir)à la fin du script.
laurent

3
Voici la même fonction déballée, bloquée par la documentation et rendue cohérente avec rmdir()et unlink(), par exemple abandonne avec E_WARNINGet retourne trueou falseindique le succès.
mindplay.dk

2
@dbf non, ce FilesystemIteratorn'est pas un itérateur récursif.
salathe

17

Supprime tous les fichiers et dossiers du chemin.

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

1
rm -rf /== recurseRmdir('/'):)
Aaron Esau

5
Veuillez noter que ce n'est pas sécurisé pour les liens symboliques! Vous avez besoin d'une vérification de cohérence après is_dir pour vérifier également qu'il s'agit de! Is_link, car sinon vous pouvez créer un lien symbolique vers un dossier externe qui est ensuite supprimé et cela peut être considéré comme une faille de sécurité. Vous devriez donc changer is_dir("$dir/$file")pouris_dir("$dir/$file") && !is_link("$dir/$file")
Kira M. Backes

13

Pour * nix, vous pouvez utiliser un shell_execpour rm -Rou DEL /S folder_namepour Windows.


2
que diriez-vous DEL /S folder_namepour Windows
ankitjaininfo

@Gordon RMDIR /S /Q folder_nameest ce qui a fonctionné pour moi
Brian Leishman

2
@ WiR3D tant que la commande exec ne contient aucune entrée utilisateur, vous devriez être bon. Ex:exec('rm -rf ' . __DIR__ . '/output/*.log');
Brian Hannay

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

Suggestion assez complexe ;-)
Philipp

@Philipp ouais je suppose. Eh bien, j'ai fait une fourchette à partir de gist.github.com/mindplay-dk/a4aad91f5a4f1283a5e2 parce que je ne l'ai pas fait fonctionner donc j'ai juste pensé que je pourrais aussi le partager.
XzaR

Il ya un problème. Il ne supprime pas les dossiers vides une fois tous les fichiers supprimés. Publication d'une version légèrement modifiée comme réponse ci-dessous.
Vladislav Rastrusny

@Vladislav Rastrusny vraiment? Ça marche pour moi. Peut-être que vous aviez un dossier en lecture seule ou autre.
XzaR


1

code `` simple '' qui fonctionne et peut être lu par un enfant de dix ans:

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

Veuillez noter que tout ce que j'ai fait était de développer / simplifier et corriger (ne fonctionnait pas pour les répertoires non vides) la solution ici: En PHP, comment supprimer récursivement tous les dossiers qui ne sont pas vides?


1

Solution améliorée de @Artefacto - fautes de frappe corrigées et code simplifié, fonctionnant à la fois pour les répertoires vides et non vides.

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

1

La solution 100% fonctionnelle

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

0

Quelque chose comme ça?

function delete_folder($folder) {
    $glob = glob($folder);
    foreach ($glob as $g) {
        if (!is_dir($g)) {
            unlink($g);
        } else {
            delete_folder("$g/*");
            rmdir($g);
        }
    }
}

Je ne peux pas expliquer pourquoi mais cela n'a pas fonctionné pour moi. Il a continué à essayer de supprimer un dossier qui n'était pas vide. La deuxième réponse ci-dessus a bien fonctionné.
laurent

1
@ buggy3 De quel code spécifique parlez-vous? Le lien renvoie simplement à cette page de questions.
cgogolin

0

Exemple avec la fonction glob () . Il supprimera tous les fichiers et dossiers de manière récursive, y compris les fichiers commençant par un point.

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

Je suis allé avecsystem('rm -fr folder')
Itay Moav -Malimovka

0

La fonction unlinkr supprime récursivement tous les dossiers et fichiers dans un chemin donné en s'assurant qu'elle ne supprime pas le script lui-même.

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 vous souhaitez supprimer tous les fichiers et dossiers où vous placez ce script, appelez-le comme suit

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

si vous voulez simplement supprimer uniquement les fichiers php, appelez-le comme suit

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

vous pouvez également utiliser n'importe quel autre chemin pour supprimer les fichiers

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

Cela supprimera tous les fichiers du répertoire home / user / temp.


0

J'utilise ce code ...

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

ou celui-ci ...

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

Est-ce récursif?
Martin Tournoij

0

Une fois les tests terminés, supprimez simplement # de la #unlink et #rmdir dans la classe.

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

?>

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

?>

0

Il semble que toutes les autres réponses supposent que le chemin donné à la fonction est toujours un répertoire. Cette variante fonctionne pour supprimer des répertoires ainsi que des fichiers uniques:

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

0

Utilisation correcte de DirectoryIterator et de la récursivité:

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

-1

Je viens de créer ce code, à partir de discussions sur StackOverflow. Je n'ai pas encore testé sur l'environnement Linux. Il est fait afin de supprimer complètement un fichier ou un répertoire:

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

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

}

-1

Variante modifiée de la solution @XzaR. Il supprime les dossiers vides lorsque tous les fichiers en sont supprimés et lève des exceptions au lieu de renvoyer false en cas d'erreurs.

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

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

1
Si vous répondez à une ancienne question qui a déjà un certain nombre de réponses, y compris une acceptée, vous devez publier une explication de la valeur ajoutée de votre réponse, pas seulement du code. Les réponses basées uniquement sur le code sont mal vues en général, mais surtout dans ce cas.
Jared Smith

J'ai voté pour cette réponse et j'ai accepté la réponse. Ce n'est pas mal, d'après mon test de référence (sans unlink, rmdir) le opendir+ readdirfonctionne plus vite scandiret RecursiveDirectoryIteratorutilise également moins de mémoire que tout. Pour supprimer le dossier que je dois d' closedirabord, j'étais coincé à cela. Merci à cette réponse.
vee
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.