先运行php -m | grep zip无输出或php -r "new ZipArchive()"报错即说明未启用;Web端查phpinfo()不准确,必须CLI实测;Ubuntu/Debian执行sudo apt install php-zip,macOS用brew install php@8.2-zip,Windows取消php.ini中;extension=php_zip.dll的注释。

确认服务器是否支持 ZipArchive 扩展
PHP 8.2 默认不自动启用 zip 扩展,即使你用 Homebrew、phpstudy 或编译安装了 PHP,ZipArchive 类仍可能不可用。直接运行 new ZipArchive 会报错 Fatal error: Uncaught Error: Class "ZipArchive" not found。
检查方法:在 Web 环境下访问 phpinfo() 页面,搜索 “zip”;或 CLI 下执行 php -m | grep zip。若无输出,说明扩展未加载。
- Linux(如 Ubuntu/Debian):
sudo apt install php-zip,然后重启php-fpm或 Apache - macOS(Homebrew):
brew install php@8.2-zip,再确认php.ini中有未注释的extension=zip - Windows(phpstudy / 手动解压版):打开
php.ini,取消;extension=php_zip.dll前的分号,重启 Web 服务
下载远程 ZIP 文件并保存到本地
不能直接用 ZipArchive::open() 打开 HTTP 地址(如 https://example.com/archive.zip),它只接受本地文件路径。必须先用 file_get_contents() 或 curl 下载到磁盘,再操作。
推荐用 curl ——更可控、支持超时和错误码判断:
立即学习“PHP免费学习笔记(深入)”;
$url = 'https://example.com/data.zip';
$localPath = '/tmp/downloaded.zip';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$file = fopen($localPath, 'wb');
curl_setopt($ch, CURLOPT_FILE, $file);
$result = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
fclose($file);
if ($result === false || $httpCode !== 200 || !file_exists($localPath) || filesize($localPath) === 0) {
throw new RuntimeException('ZIP 下载失败');
}
注意:/tmp 目录需有写权限;生产环境建议加随机后缀防覆盖,如 sprintf('/tmp/%s.zip', bin2hex(random_bytes(6)))。
解压 ZIP 到指定目录并处理路径安全
直接调用 $zip->extractTo() 有路径穿越风险(比如 ZIP 内含 ../../../etc/passwd)。PHP 8.2 的 ZipArchive 不自动过滤,必须手动校验每个文件名。
标准做法是:遍历 ZIP 中所有条目,用 realpath() + dirname() 检查是否落在目标目录内:
$zip = new ZipArchive();
$res = $zip->open($localPath);
if ($res !== true) {
throw new RuntimeException("ZIP 打开失败,错误码: $res");
}
$destDir = '/var/www/uploads/unzipped/';
mkdir($destDir, 0755, true);
for ($i = 0; $i < $zip->numFiles; $i++) {
$name = $zip->getNameIndex($i);
$safePath = rtrim($destDir, '/') . '/' . ltrim($name, '/');
$realPath = realpath($safePath);
// 关键校验:确保 $realPath 确实位于 $destDir 下
if ($realPath === false || strpos($realPath, realpath($destDir)) !== 0) {
continue; // 跳过危险路径
}
if (substr($name, -1) === '/') {
mkdir($safePath, 0755, true);
} else {
$zip->extractTo($destDir, [$name]);
}
}
$zip->close();
unlink($localPath); // 下载完立即删临时 ZIP
遗漏这点,攻击者上传恶意 ZIP 就能写入任意位置(如 ./.user.ini 或 ../webshell.php)。
遇到中文文件名乱码怎么办
PHP 8.2 的 ZipArchive 在 Linux/macOS 上默认按 Latin-1 解析文件名,而大多数 ZIP 工具(如 Windows 自带压缩、Mac 归档工具)用 UTF-8 存储中文名,导致解压后文件名变成问号或方块。
目前没有跨平台通用修复方案,但可尝试以下组合:
- 先用
$zip->getFromIndex($i)读取原始二进制名,再用mb_convert_encoding($rawName, 'UTF-8', 'GB18030')或iconv('GBK', 'UTF-8//IGNORE', $rawName)转码(取决于源系统) - 更可靠的做法:改用
exec('unzip', ...)调用系统unzip命令,并加参数-O UTF-8(Linux)或-X(macOS) - 终极建议:前端上传前强制 ZIP 文件名用英文,服务端生成统一命名规则,绕过此问题
路径校验和编码处理这两步,90% 的线上 ZIP 解压故障都出在这儿——不是扩展没开,就是没拦住 ../,或者中文名一解压就变乱码。



















