copy()函数仅支持文件路径,不支持目录参数,传入目录会直接报错;复制目录需手动递归实现遍历、建目录、逐文件复制;中文路径、权限、远程url等均为额外限制;复杂场景建议改用recursiveiteratoriterator等更健壮方案。

copy() 函数根本不支持目录参数
PHP 的 copy() 函数签名是 copy(string $source, string $dest),它只接受**文件路径**作为第一个参数。一旦传入目录路径(比如 '/var/www/uploads/'),PHP 会立刻抛出致命警告:Warning: copy(): The first argument to copy() function cannot be a directory。这不是权限或配置问题,而是函数设计上的硬性限制——它连判断目录的逻辑都没有,直接拒绝执行。
想复制整个目录?必须自己写递归逻辑
没有内置替代方案,copy() 就是单文件工具。要复制目录,你得手动实现三件事:遍历源目录、创建目标子目录结构、逐个调用 copy() 复制文件。常见错误是只用 scandir() 遍历后直接传给 copy(),结果遇到 '.'、'..' 或子目录名就崩。
- 先用
is_file($path)过滤出真正可复制的条目(file_exists()或is_dir()都不够准) - 对每个子目录,用
mkdir($dest_subdir, 0755, true)创建对应路径(第三个参数true允许递归建多级) - 递归调用自身处理子目录,而不是试图让
copy()做它做不到的事
中文路径、权限、远程 URL 都是额外雷区
即使你绕过了目录误传问题,copy() 在真实环境里还常卡在这些地方:
- Windows 下中文路径可能因编码不一致失败,
iconv('utf-8', 'gbk', $path)有时能救急,但更稳的是统一用realpath()归一化路径 - 目标目录父路径必须存在且
is_writable()返回 true;copy()不会帮你建中间目录,也不会提示“目录不存在”,只会静默失败 -
$source不能是http://地址——除非allow_url_fopen=On且封装协议支持,但即便如此,也仅限读取内容,无法保留元数据
别硬扛,该换方案时就换
如果需求明确是“完整镜像一个目录”,包括权限、时间戳、符号链接等,copy() + 手动递归只是勉强可用,远不如系统命令可靠。Linux 下可考虑 exec('cp -r --preserve=all ' . escapeshellarg($src) . ' ' . escapeshellarg($dst)),但要注意 escapeshellarg() 防注入,且 Web 环境下禁用 exec 很常见。真正需要跨平台又健壮的场景,建议改用 RecursiveIteratorIterator + stream_copy_to_stream() 组合,它比裸 copy() 更可控,也更容易加进度反馈和中断逻辑。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











