最稳方案是用RecursiveDirectoryIterator+RecursiveIteratorIterator手动递归复制:先mkdir($dst,0755,true),再遍历判断isFile()/isDir()分别处理,支持跨平台、中文路径、空目录,不依赖exec。

PHP复制整个文件夹:用 RecursiveCopyIterator 最稳
原生 PHP 没有直接的 copy_dir() 函数,靠 copy() + 递归遍历才能完成。最可靠的方式是组合 RecursiveDirectoryIterator 和 RecursiveIteratorIterator,手动创建目标路径并逐个复制文件与子目录。
别用网上流传的“一行 exec('cp -r')”方案——它依赖系统命令、不可移植、有安全风险(尤其当路径含用户输入时),且 Windows 完全不兼容。
- 必须先用
mkdir($dst, 0755, true)创建目标目录(true表示递归创建父级) - 遍历时跳过
.和..,否则会无限递归或报错 - 判断当前项是文件还是目录:用
$item->isFile()和$item->isDir(),别用is_file()等函数——迭代器对象本身已封装状态,更准确
简单可用的封装函数:copy_directory()
以下函数经实测可跨 Windows / Linux 使用,支持空子目录、中文路径(需 PHP 文件系统编码一致),不依赖外部命令:
function copy_directory(string $src, string $dst): bool
{
if (!is_dir($src)) {
return false;
}
if (!mkdir($dst, 0755, true) && !is_dir($dst)) {
return false;
}
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($src, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $item) {
$relPath = $iterator->getSubPathName();
$dstPath = $dst . DIRECTORY_SEPARATOR . $relPath;
if ($item->isDir()) {
mkdir($dstPath, 0755, true);
} else {
if (!copy($item->getPathname(), $dstPath)) {
return false;
}
}
}
return true;
}
调用示例:copy_directory('/path/to/src', '/path/to/dst');
立即学习“PHP免费学习笔记(深入)”;
第一步】:将安装包中所有的文件夹和文件用ftp工具以二进制方式上传至服务器空间;(如果您不知如何设置ftp工具的二进制方式,可以查看:(http://www.shopex.cn/support/qa/setup.help.717.html)【第二步】:在浏览器中输入 http://您的商店域名/install 进行安装界面进行安装即可。【第二步】:登录后台,工具箱里恢复数据管理后台是url/sho
注意:RecursiveIteratorIterator::SELF_FIRST 确保目录先于其内容被处理,否则 mkdir 可能失败。
遇到 Permission denied 或 Operation not permitted 怎么办
这类错误通常不是代码问题,而是权限或系统限制:
- 目标路径所在分区是否为只读(如挂载的 NTFS 分区在 Linux 下默认无写权限)
- 源目录中是否有符号链接?
RecursiveDirectoryIterator默认不跟随,若需复制链接本身,加标志RecursiveDirectoryIterator::FOLLOW_SYMLINKS - SELinux 或 macOS 的 SIP(System Integrity Protection)可能拦截操作,临时禁用仅用于调试,生产环境应调整策略而非关闭保护
- Windows 上某些系统文件(如
Thumbs.db或回收站$RECYCLE.BIN)无法读取,需在循环中用try/catch捕获RuntimeException并跳过
大文件夹复制慢?别盲目加 set_time_limit(0)
超时常见,但根本原因常被忽略:
- PHP 默认单次脚本执行时间 30 秒,复制上万小文件容易超时——此时应优化为分批处理(例如每 100 个文件
usleep(10000)防卡死),而非直接关超时 - 频繁调用
copy()开销大,可改用file_get_contents()+file_put_contents()组合,对小文件略快;但大文件会吃光内存,务必加stream_copy_to_stream()流式复制 - 如果只是部署用途,更推荐用 rsync 或 Git 钩子代替运行时 PHP 复制——PHP 不是干这个的
真正难的从来不是“怎么写”,而是判断该不该在 PHP 里做这件事。路径权限、符号链接、稀疏文件、ACL 属性……这些细节一旦漏掉,线上就静默失败。










