php7中用scandir+mkdir+copy递归复制目录的最稳妥写法是:先用mkdir($dst, 0755, true)创建目标目录(必须显式启用recursive),再用scandir获取源目录项并array_diff过滤'.','..',对每个子项判断类型——目录则递归调用自身,文件则copy复制;不可用copy处理目录或shell_exec('cp -r'),因前者报错、后者跨平台兼容性差且存在安全与路径解析风险。

PHP7 中用 scandir + mkdir + copy 实现递归复制
直接上最稳妥的写法:不用 RecursiveIteratorIterator 那套复杂对象,避免路径处理出错或权限异常中断。核心是手动遍历子项、判断类型、分别处理。
关键点在于:scandir 返回的数组含 . 和 ..,必须过滤;目标目录要先创建(mkdir 要加 recursive 参数);文件才调用 copy,目录则递归调用自身。
示例函数:
function copyDir($src, $dst) {
if (!is_dir($dst)) {
mkdir($dst, 0755, true);
}
$files = array_diff(scandir($src), ['.', '..']);
foreach ($files as $file) {
$srcPath = $src . '/' . $file;
$dstPath = $dst . '/' . $file;
if (is_dir($srcPath)) {
copyDir($srcPath, $dstPath);
} else {
copy($srcPath, $dstPath);
}
}
}
为什么不能只用 copy 或 shell_exec('cp -r')
copy 只支持文件,传入目录会直接失败并报 Warning:copy(): The first argument to copy() must be a valid path to a file。硬用会导致静默跳过或脚本中断。
用 shell_exec('cp -r') 看似简单,但有三个实际坑:
- Linux/macOS 下可用,Windows 无
cp命令,xcopy语法又不兼容 - Web 服务器(如 Apache/Nginx)常禁用执行系统命令,
shell_exec返回空或 false - 路径含空格、中文、特殊符号时容易被 shell 解析错误,导致复制不全或路径错乱
mkdir 的第三个参数 true 必须显式传入
PHP7 默认 mkdir 不支持递归创建多级目录。比如目标是 /a/b/c,而 /a/b 不存在,不加 true 就会报错:Warning: mkdir(): No such file or directory。
这个参数在 PHP5.6+ 才稳定支持,PHP7 完全可用,但容易被忽略——尤其从旧代码迁移时,只写 mkdir($dst) 会卡在第一层子目录就失败。
另外注意权限掩码:0755 是常规选择;若目标需写入(如日志目录),可改用 0775,但别用 0777,有安全风险。
遇到 Permission denied 或 Operation not permitted 怎么办
常见于三类场景:
- 源目录含符号链接(symlink),
is_dir判定为真,但scandir读取时权限不足 → 加if (!is_link($srcPath))过滤 - 目标磁盘空间不足或 inodes 耗尽 → 复制前用
disk_free_space($dst)粗略检查 - SELinux 或 macOS Gatekeeper 限制 Web 进程访问某些路径(如用户主目录)→ 改用 Web 服务可写的路径,例如
/var/www/tmp或sys_get_temp_dir()
调试时建议在循环里加 error_log("Copying $srcPath -> $dstPath");,配合 tail -f /var/log/apache2/error.log 快速定位卡在哪一步。
递归深度大时(比如上万文件),PHP 默认栈深度可能不够,但实际中极少触发;真遇到,优先检查是不是软链形成环路,而不是急着调 xdebug.max_nesting_level。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











