必须使用BLOB类二进制安全字段存储gzdeflate压缩数据,若用VARCHAR则需base64_encode后存入;解压前须校验输入合法性,因gzinflate对非法输入会静默失败。

存储前必须确认字段类型支持二进制数据
MySQL 的 VARCHAR 或 TEXT 类型默认按字符集(如 utf8mb4)解析,遇到 gzdeflate 输出的二进制字节(含 \x00、\xff 等非法 UTF-8 序列)会截断或报错。实际写入时可能只存了前几个字节,解压时直接 gzinflate() 失败并返回 false。
正确做法是使用二进制安全字段:
-
BLOB、TINYBLOB、MEDIUMBLOB—— 推荐,原样存储不转码 - 若必须用字符串字段,先
base64_encode(gzdeflate($str)),再存进VARCHAR;解压时反向操作 - 避免用
ENUM、SET或带COLLATION的TEXT字段,它们会在 INSERT/SELECT 时强制字符转换
解压前务必校验输入是否为合法 deflate 数据
gzinflate() 对非 deflate 输入(比如空字符串、被截断的压缩流、明文)会静默失败或触发警告,生产环境不能依赖它自动兜底。
应主动检查魔数和长度:
立即学习“PHP免费学习笔记(深入)”;
- deflate 流无固定头部,但可快速排除明显非法输入:
if (!is_string($data) || strlen($data) - 更稳妥的做法是加一层
@gzinflate($data)并判断返回值:$raw = @gzinflate($data); if ($raw === false) { /* 拒绝解压,记录日志 */ } - 若字段内容来自不可信来源(如用户提交、Redis 缓存),建议在解压前加
substr($data, 0, 2) !== "\x78\x9c" && substr($data, 0, 2) !== "\x78\x01" && substr($data, 0, 2) !== "\x78\xda"初筛(常见 zlib header 前缀)
压缩级别设置影响性能与体积,PHP7.4 默认是 -1
gzdeflate($str, $level) 的 $level 参数范围是 -1(默认,自适应)到 9(最高压缩)。但注意:
-
level = -1在 PHP7.4 中实际调用 zlib 的Z_DEFAULT_COMPRESSION,对短文本( -
level = 1压缩快、体积稍大;level = 6是吞吐与体积的常见平衡点;level = 9显著拖慢压缩速度,对小文本收益极低 - 数据库字段越长,IO 和索引开销越大 —— 不要盲目设
9,尤其当原始字符串本身就很短(如 JSON 配置项)
跨语言解压需警惕 deflate 封装差异
PHP 的 gzdeflate() 输出的是纯 DEFLATE 数据(RFC 1951),不含 zlib 或 gzip 头。这点和 Python 的 zlib.compress()(默认也是 raw deflate)一致,但和 gzip.compress()(带 gzip header)不兼容。
若其他服务(如 Go、Node.js)要解压 PHP 存的 gzdeflate 结果,必须明确指定 “raw deflate” 模式:
- Go:用
zlib.NewReader(bytes.NewReader(data), zlib.NoHeader) - Node.js:
zlib.inflate(rawBuffer, callback)(不是gunzip) - Python:
zlib.decompress(data, -zlib.MAX_WBITS)(负数参数表示忽略 header) - 反过来,如果其他语言用了 gzip 格式存库,PHP 就得用
gzdecode(),不能用gzinflate()
最易被忽略的一点:没有统一约定格式的多语言系统里,光看函数名(比如都叫 “compress”)根本无法判断输出是什么——必须在协议层明确标注压缩算法和封装类型,否则上线后解压失败很难定位。



















