php copy() 不保留元数据是设计使然,仅复制内容和权限;类unix系统可用shell_exec('cp -p')保留时间戳、权限、所有权等,windows则基本无法实现完整元数据拷贝。

PHP copy() 不能保留元数据,这是设计使然
PHP 的 copy() 函数只复制文件内容和权限(umask 影响下的目标权限),不触碰修改时间、访问时间、所有者、组、扩展属性等任何元数据。这不是 bug,是 POSIX 层面的限制 —— PHP 本身没有提供系统调用级的元数据拷贝接口。
用 shell_exec() 调用 cp -p 是最直接的方案
Linux/macOS 下,cp -p 会尽可能保留所有权、时间戳、权限、ACL 和扩展属性(需 root 或 CAP_SYS_ADMIN)。Windows 不支持该语义,此法仅限类 Unix 环境。
-
cp -p的-p等价于--preserve=mode,ownership,timestamps,部分系统还默认包含context,links,xattr - 必须确保 PHP 进程有权限执行
cp且源/目标路径可被 shell 访问(注意open_basedir和disable_functions限制) - 路径含空格或特殊字符时,务必用
escapeshellarg()包裹:shell_exec('cp -p ' . escapeshellarg($src) . ' ' . escapeshellarg($dst)) - 失败时返回
null或空字符串,建议检查shell_exec('cp -p ... 2>&1')捕获错误输出
touch() + chown() + chmod() 手动还原关键元数据
当无法执行 shell 命令(如共享主机、容器无 cp)、或只需保留部分属性(如仅时间戳+权限)时,可用 PHP 原生函数组合模拟。
- 先
copy()文件内容 - 用
filemtime()/fileatime()获取源文件时间戳,再用touch($dst, $mtime, $atime)写入目标 - 用
fileowner()/filegroup()+chown()/chgrp()设置归属(注意:PHP 进程需有chown权限,通常仅限 root 或文件所有者) - 权限用
chmod($dst, fileperms($src)),但注意fileperms()返回的是带文件类型位的整数,实际传给chmod()应用& 0777掩码
Windows 下基本无解,别强求“完全一致”
Windows NTFS 的“创建时间”“最后访问时间”“最后写入时间”可通过 touch() 设置,但“所有者”“ACL”“硬链接”“重解析点”等无法用 PHP 原生函数控制。即使调用 cmd /c copy,也不等价于 cp -p。
立即学习“PHP免费学习笔记(深入)”;
- PHP 在 Windows 上的
chown()/chgrp()恒返回false,属于无效操作 -
fileowner()和filegroup()在 Windows 返回0(伪值),不可信 - 若业务强依赖完整元数据(如审计日志、备份一致性),应避免在 Windows 环境做跨机器元数据敏感的拷贝
cp -p;能就用它,干净利落。不能的话,按需补时间戳和权限,别硬凑所有权——尤其在线上生产环境,chown() 失败可能比不设更危险。











