Excluir diretório com arquivos nele?

245

Gostaria de saber, qual é a maneira mais fácil de excluir um diretório com todos os seus arquivos?

Estou usando rmdir(PATH . '/' . $value);para excluir uma pasta, no entanto, se houver arquivos nela, simplesmente não posso excluí-la.

mate
fonte
2
Sim, respondeu exatamente nessa pergunta.
timdev
Só quero notar. Criei vários arquivos e se durante o processo receber algum erro, será necessário excluir os arquivos criados anteriormente. Quando arquivos criados, esqueci de usar fclose($create_file);e quando excluir, consegui Warning: unlink(created_file.xml): Permission denied in.... Portanto, para evitar esses erros, feche os arquivos criados.
Andris

Respostas:

381

Existem pelo menos duas opções disponíveis hoje em dia.

  1. Antes de excluir a pasta, exclua todos os seus arquivos e pastas (e isso significa recursão!). Aqui está um exemplo:

    public static function deleteDir($dirPath) {
        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)) {
                self::deleteDir($file);
            } else {
                unlink($file);
            }
        }
        rmdir($dirPath);
    }
    
  2. E se você estiver usando o 5.2+, poderá usar um RecursiveIterator para fazer isso sem implementar a recursão:

    $dir = 'samples' . DIRECTORY_SEPARATOR . 'sampledirtree';
    $it = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
    $files = new RecursiveIteratorIterator($it,
                 RecursiveIteratorIterator::CHILD_FIRST);
    foreach($files as $file) {
        if ($file->isDir()){
            rmdir($file->getRealPath());
        } else {
            unlink($file->getRealPath());
        }
    }
    rmdir($dir);
    
alcuadrado
fonte
11
Sua segunda implementação é um tanto perigosa: ela não verifica pontos ( .e ..) e exclui o caminho resolvido, não o caminho real.
Alix Axel
9
pequeno complemento :-) glob () não suporta arquivos como .htaccess. Eu usei a função para limpar diretórios criados pelo KCFinder (plugin CKEditor), que gera .htaccess e .thumbs (arquivo + pasta). Em vez disso, usei a scandirfunção para obter a lista de pastas. Apenas certifique-se de filtrar o '.' e '..' da lista de resultados.
Joshua - Pendo
25
DIRECTORY_SEPARATOR não é necessário quando você está criando caminhos para enviar para o sistema operacional. O Windows também aceitará barras. É principalmente útil para explode()um caminho retirado do sistema operacional. alanhogan.com/tips/php/directory-separator-not-necessary
ReactiveRaven
5
Além do @Alix Axel Usar aqui o [SplFileInfo :: getRealPath ()] ( php.net/manual/en/splfileinfo.getrealpath.php ) não é uma boa ideia. Esse método expande todos os links simbólicos, ou seja, será excluído um arquivo real de algum lugar, em vez de um link simbólico do diretório de destino. Você deve usar SplFileInfo :: getPathname ().
Vijit
2
Concordo com @Vijit, use getPathname () em vez de getRealPath (). Ele faz a mesma coisa sem excluir mais do que você espera, se forem encontrados links simbólicos.
precisa saber é o seguinte
196

Eu geralmente uso isso para excluir todos os arquivos em uma pasta:

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

E então você pode fazer

rmdir($dirname);
user3033886
fonte
26
Isso não exclui pastas recursivamente; só funciona se a pasta tiver apenas arquivos regulares, todos com extensões.
Mgnb
5
Se nenhuma recursão for necessária, esta é a melhor e mais simples resposta até agora. Obrigado!
eisbehr
2
Para remover todos os arquivos de uma pasta, não apenas os que possuem extensões, use glob da seguinte maneira: array_map('unlink', glob("$dirname/*"));Isso ainda não permite excluir diretórios aninhados na pasta.
Kremuwa # 9/18
Observe que isso também removerá arquivos de ponto (oculto).
BadHorsie 26/09/19
85

Qual é a maneira mais fácil de excluir um diretório com todos os seus arquivos?

system("rm -rf ".escapeshellarg($dir));
Seu senso comum
fonte
33
Espero que você não esteja falando sério. O que acontece se $ dir for /
The Pixel Developer
108
@ Exatamente o mesmo que com qualquer um dos códigos acima. Não é?
Seu senso comum
7
Observe que, dependendo de como $diré gerado / fornecido, pode ser necessário fazer um pré-processamento adicional para garantir a segurança e evitar bugs. Por exemplo, se houver $dirum espaço sem escape ou ponto e vírgula, poderá haver efeitos colaterais indesejáveis. Este não é o caso das respostas que usam coisas como, rmdir()porque manipularão os caracteres especiais para você.
308 Trott
5
Versão do Windows:system('rmdir '.escapeshellarg($path).' /s /q');
Cypher
2
@ThePixelDeveloper, você não deve se preocupar em excluir /, isso só funcionaria se você lounch o script na linha de comando como raiz, porque na Web tudo acontece como usuário apache
Ben
49

Função curta que faz o trabalho:

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

Eu o uso em uma classe Utils como esta:

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

Com grande poder, vem uma grande responsabilidade : Quando você chama esta função com um valor vazio, ela exclui os arquivos iniciados em root ( /). Como salvaguarda, você pode verificar se o caminho está vazio:

function deleteDir($path) {
    if (empty($path)) { 
        return false;
    }
    return is_file($path) ?
            @unlink($path) :
            array_map(__FUNCTION__, glob($path.'/*')) == @rmdir($path);
}
Blaise
fonte
1
O estático não funciona porque $ this === NULL quando você chama uma função estática em uma classe. Funcionaria se$this_func = array(__CLASS__, __FUNCTION__);
Matt Connolly
2
Alguém pode explicar a linha array_map($class_func, glob($path.'/*')) == @rmdir($path)? Eu acho que ele está recorrendo pelas subpastas, mas o que a parte == @rmdir faz? Como o <array de booleanos> == <boolean> retorna a resposta? Verifica se cada valor de retorno da recursão é igual ao booleano à direita?
arviman
2
É um truque para mesclar duas declarações em uma declaração. Isso ocorre porque os operadores ternários permitem apenas uma declaração por argumento. array_map(...)remove todos os arquivos dentro do diretório, @rmdir(...)remove o próprio diretório.
Blaise
3
Seja cuidadoso! Esta função não verifica se o caminho realmente existe. Se você passar um argumento vazio, a função começará a excluir arquivos a partir da raiz! Adicione uma verificação de integridade ao seu caminho antes de executar esta função.
Tatu Ulmanen
3
Algumas pessoas não viram o comentário de Tatu e foram excluídas recursivamente /, então anexei uma versão protegida ao meu post.
Blaise
22

Como visto no comentário mais votado na página de manual do PHP sobre rmdir()(consulte http://php.net/manual/es/function.rmdir.php ), a glob()função não retorna arquivos ocultos. scandir()é fornecido como uma alternativa que resolve esse problema.

O algoritmo descrito lá (que funcionou como um encanto no meu caso) é:

<?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); 
    } 
?>
Latorre alemão
fonte
você pode explicar o is_dir ("$ dir / $ file") - não encontrou o parâmetro "$ dir / $ file"
Igor L.
O que você quer dizer? Ele verifica se a entrada encontrada em um diretório ( $file) é um diretório ou um arquivo. "$dir/$file"é o mesmo que $dir . "/" . $file.
German Latorre
Sinceramente, eu não sabia que você pode concatenar variáveis ​​como essa :) thx
Igor L.
18

Esta é uma versão mais curta funciona muito bem para mim

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);
    }
}
Playnox
fonte
15

Você pode usar o sistema de arquivos do Symfony ( código ):

// composer require symfony/filesystem

use Symfony\Component\Filesystem\Filesystem;

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

No entanto, não consegui excluir algumas estruturas de diretório complexas com esse método; portanto, primeiro você deve experimentá-lo para garantir que esteja funcionando corretamente.


Eu poderia excluir a estrutura de diretórios mencionada usando uma implementação específica do Windows:

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


E apenas por uma questão de integridade, aqui está um antigo código meu:

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);
}
Gras Double
fonte
Muito obrigado. Você economiza meu tempo.
Zarif khan
"Não reinvente a roda" . Obrigado
Kamafeather
9

Aqui você tem uma recursão simples e agradável para excluir todos os arquivos no diretório de origem, incluindo esse diretório:

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

}

A função é baseada na recursão feita para copiar o diretório. Você pode encontrar essa função aqui: Copie todo o conteúdo de um diretório para outro usando php

Tommz
fonte
4

A melhor solução para mim

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

código:

function my_folder_delete($path) {
    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);
    }
}

ps LEMBRE-SE!
não passe valores vazios para nenhuma função de exclusão de diretório !!! (faça backup sempre, caso contrário, um dia você poderá sofrer um desastre!)

T.Todua
fonte
4

Que tal isso:

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);
}
adrian
fonte
4

A função Glob não retorna os arquivos ocultos; portanto, o scandir pode ser mais útil ao tentar excluir recursivamente uma árvore.

<?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);
  }
?>
castiçal
fonte
4

Você pode tentar da seguinte maneira:

/*
 * 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);
        }
    }
}
Bablu Ahmed
fonte
3

Eu prefiro isso porque ele ainda retorna VERDADEIRO quando for bem-sucedido e FALSO quando falhar, e também evita um erro em que um caminho vazio pode tentar excluir tudo de '/ *' !!:

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);
}
Matt Connolly
fonte
3

Quero expandir a resposta de @alcuadrado com o comentário de @Vijit para lidar com links simbólicos. Primeiro, use getRealPath (). Porém, se você tiver quaisquer links simbólicos que sejam pastas, ela falhará, pois tentará chamar o rmdir em um link - portanto, você precisará de uma verificação extra.

$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);
user701152
fonte
1
Não tenho representante suficiente para comentar a resposta diretamente.
user701152
3

Usando o DirectoryIterator, o equivalente a uma resposta anterior…

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);
}
Alan Trewartha
fonte
3

Este funciona para mim:

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

Algo assim?

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
fonte
2

Um pouco de modificação no código do alcuadrado - globnão vejo arquivos com nome de pontos como, .htaccessentão eu uso o scandir e o script se apaga - verifique __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);
}
karma_police
fonte
2

Exemplo para o servidor Linux: exec('rm -f -r ' . $cache_folder . '/*');

realmag777
fonte
Eu normalmente gosto de adicionar uma verificação de integridade em $ cache_folder antes de executar rm -rf para evitar erros dispendiosos
glifo
1

Excluir todos os arquivos na pasta
array_map('unlink', glob("$directory/*.*"));
Excluir todos. * - Arquivos na pasta (sem: "." E "..")
array_map('unlink', array_diff(glob("$directory/.*),array("$directory/.","$directory/..")))
Agora exclua a própria pasta
rmdir($directory)

PP2000
fonte
1

2 centavos para adicionar a ESTA resposta acima, o que é ótimo

Depois que sua função glob (ou similar) digitalizar / ler o diretório, adicione um condicional para verificar se a resposta não está vazia ou um invalid argument supplied for foreach()aviso será emitido. Assim...

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

Minha função completa (como um método de objeto):

    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()
Phil Meadows
fonte
1

Aqui está a solução que funciona perfeitamente:

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;
}
Tarik
fonte
1

Você pode copiar os ajudantes da YII

$ directory (string) - a ser excluído recursivamente.

$ options (array) - para a remoção do diretório. As opções válidas são: traverseSymlinks: booleano, se links simbólicos para os diretórios também devem ser atravessados. O padrão falseé que significa que o conteúdo do diretório com link simbólico não seria excluído. Somente o link simbólico seria removido nesse caso padrão.

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);
    }
}
José Veríssimo
fonte
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);
  }
 }
?>

Já experimentou o código obove em php.net

Trabalhar para mim bem

Gaurang
fonte
0

Para Windows:

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

Como a solução da Playnox, mas com o elegante DirectoryIterator interno:

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!');
  }
 }
Matthew Slyman
fonte
0

Não me lembro de onde copiei essa função, mas parece que não está listada e está funcionando para mim

function rm_rf($path) {
    if (@is_dir($path) && is_writable($path)) {
        $dp = opendir($path);
        while ($ent = readdir($dp)) {
            if ($ent == '.' || $ent == '..') {
                continue;
            }
            $file = $path . DIRECTORY_SEPARATOR . $ent;
            if (@is_dir($file)) {
                rm_rf($file);
            } elseif (is_writable($file)) {
                unlink($file);
            } else {
                echo $file . "is not writable and cannot be removed. Please fix the permission or select a new path.\n";
            }
        }
        closedir($dp);
        return rmdir($path);
    } else {
        return @unlink($path);
    }
}
dav
fonte
0

Simples e Fácil ...

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

Que tal isso?

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

Mohamad Hamouday
fonte
0

Se você não tiver certeza, determinado caminho é diretório ou arquivo, você pode usar esta função para excluir o caminho

function deletePath($path) {
        if(is_file($path)){
            unlink($path);
        } elseif(is_dir($path)){
            $path = (substr($path, -1) !== DIRECTORY_SEPARATOR) ? $path . DIRECTORY_SEPARATOR : $path;
            $files = glob($path . '*');
            foreach ($files as $file) {
                deleteDirPath($file);
            }
            rmdir($path);
        } else {
            return false;
        }
}
Jay Padaliya
fonte