ZipArchive::addGlob()在PHP 8.1中默认仅匹配当前工作目录下文件,不递归、不支持**,且路径相对于getcwd()而非指定目录,失败静默返回false,无法保留目录结构。

PHP 8.1 中 ZipArchive::addGlob() 不能直接“调用”出目录结构,它默认只匹配当前工作目录(getcwd())下的文件,且不递归、不支持 **,误用会导致压缩包为空或漏文件。
为什么 addGlob() 在 PHP 8.1 里经常没反应
addGlob() 的路径参数不是“源目录下的 glob 表达式”,而是“相对于 getcwd() 的 glob 路径”。如果你在 /var/www/app 下运行脚本,却传入 addGlob('src/*.php'),它实际查找的是 /var/www/app/src/*.php —— 但如果脚本不在该目录执行,就什么都匹配不到。
- 它不会自动切换到目标目录,也不会递归进入子目录(哪怕你写
src/**/*.php,PHP 8.1 默认也忽略**) - 返回值是
bool,但失败时静默返回false,不抛异常,也不报错 - 即使匹配到文件,
addGlob()默认把所有文件扁平化塞进 ZIP 根目录,不保留原始路径层级
如何让 addGlob() 正确保留 src/ 和 tests/ 目录结构
必须手动构造每个匹配文件在 ZIP 内的相对路径,并逐个调用 addFile() —— addGlob() 本身不提供路径重映射能力。可行做法是:
- 先用
glob($pattern, GLOB_BRACE | GLOB_ONLYDIR)或scandir()获取真实路径列表 - 对每个匹配项,用
realpath()+str_replace($baseDir . '/', '', $fullPath)截出 ZIP 内路径 - 再用
$zip->addFile($fullPath, $zipPath)显式添加(注意:第二个参数不能以/开头) - 空目录需额外调用
$zip->addEmptyDir($dirName)(PHP 7.2+ 支持)
示例关键片段:
修复 Core bailout 状态损坏安全问题,以及 BZ2、DOM、PDO、Opcache/JIT 内存与执行错误,改进 Unicode 字素处理、URL 解析、TLS 流状态、信号分发及会话错误处理。
$baseDir = '/var/www/project';
$zip->open('project.zip', ZipArchive::CREATE);
foreach (glob($baseDir . '/src/*.php') as $file) {
$relPath = str_replace($baseDir . '/', '', $file);
$zip->addFile($file, $relPath); // 如变成 'src/index.php'
}
$zip->close();
PHP 8.1 下更稳的替代方案:用 RecursiveIteratorIterator
比起依赖 addGlob() 的模糊行为,直接遍历更可控,尤其对嵌套目录、权限异常项、中文路径等场景:
- 用
new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS))安全遍历 - 对每个
$file,检查$file->isFile()(跳过目录项),再用$file->getRealPath()获取绝对路径 - 构造 ZIP 内路径时,统一用
str_replace(realpath($dir) . DIRECTORY_SEPARATOR, '', $file->getRealPath()) - 调用
setCompressionName($relPath, ZipArchive::CM_DEFLATE, 6)对文本类文件启用中等压缩 - 遇到中文名,确保源路径和
addFile()第二个参数都是 UTF-8 编码(Linux/macOS 默认安全;Windows CLI 解压工具可能需转 GBK,此时改用addFromString())
容易被忽略的 close() 失败问题
$zip->close() 返回 false 并不罕见,但它意味着 ZIP 文件根本没写成——而多数人只检查 open(),忽略这个最终落盘步骤:
- 磁盘满、临时目录无写权限、ZIP 文件正被浏览器下载中(HTTP 流未结束),都会导致
close()失败 - 错误码不会暴露,只能靠
file_exists($zipFile)+filesize($zipFile) > 0双重确认 - 若失败,不要静默吞掉,应记录日志并清理残留空文件:
if (!$zip->close() || !file_exists($zipFile) || filesize($zipFile) === 0) { @unlink($zipFile); throw new RuntimeException('ZIP 写入失败'); }
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










