PHP 8.5 运维/调试/性能改动:curl 跨请求复用、OPcache 只读缓存、致命错误默认 backtrace
2025 年底发布的 PHP 8.5 里,有一批和运维、调试、性能直接相关的改动,其中几项由 Tideways 工程师提交或参与。下面按性能、运维、调试三块整理。
性能
$array === [] 变快
8.5 为 $array === [] 增加了专门 opcode 优化。此前同样是检查空数组,!$array、count($array) === 0、empty($array) 都比它快;8.5 里 $array === [] 反超成最快。
不要把已有代码全部改成这种写法。做微优化时,优势可能在下个版本被新一轮引擎改动抹平,甚至换一条代码路径变快。更合适的做法是写可读性最好的代码,把性能收益留给未来引擎改进。
match(true) 编译期优化
8.5 减少了 match(true) 生成的 opcode 数量。人造示例基准测试显示性能提升约 17%(±6%)。
这类写法常见于多条件分发,例如用 !!preg_match() 匹配 Welcome/Hello 时返回 en,匹配 Bienvenue/Bonjour 时返回 fr,其余走 default。
持久化 cURL 句柄:复用 DNS、连接和 SSL 握手
PHP 的无共享架构能避免内存泄漏和用户间数据意外泄露,但代价是每次请求都要重复做同样的底层工作:HTTP 请求里的 DNS 解析、连接建立、SSL 握手。
8.5 新增 curl_share_init_persistent(),让同一 PHP 进程在不同请求之间共享这些信息:
$sh = curl_share_init_persistent([
CURL_LOCK_DATA_CONNECT,
CURL_LOCK_DATA_SSL_SESSION,
CURL_LOCK_DATA_DNS,
]);
$ch = curl_init("https://tideways.com");
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SHARE, $sh);
curl_exec($ch);
执行后用 curl_getinfo() 遍历 *_time 各阶段耗时字段即可对比效果。该改动由 Eric Norris 通过 Add persistent curl share handles 和 Persistent curl share handle improvement 两个 RFC 贡献。
异常和错误实例化更快
Niels Dossche 的 PR 将部分检查移到 debug builds,生产构建会跳过,因此异常和错误的实例化过程略有提速。
其他函数级优化
以下函数在 8.5 有性能改进:
array_find()array_filter()array_reduce()usort()/uasort()str_pad()implode()pack()ReflectionProperty::getValue()及其变体
运维
OPcache 成为必选扩展
8.5 起 OPcache 自动内置到每个 PHP 二进制,不再存在“不带 OPcache 运行 PHP”的选项。官方 php Docker 镜像过去经常因为忘记安装 OPcache 而出问题,现在这个问题从源头消失。这项改动主要是简化维护,让更多环境直接利用 OPcache 优化器。OPcache 仍可通过 php.ini 禁用。
OPcache 文件缓存支持只读
8.5 之前,OPcache 文件缓存无法和只读文件系统一起使用,这导致部署与运行分离的容器环境没法受益于文件缓存,例如 AWS Lambda 上的 Bref。冷启动时间正是这类场景重点优化的目标。
Samuel 的 PR 新增 INI 选项 opcache.file_cache_read_only。开启后 OPcache 不再执行会让文件缓存失效或修改文件系统的写入操作。想获得最佳效果,建议配合:
opcache.validate_timestamps=0
opcache.enable_file_override=1
opcache.file_cache_consistency_checks=0
PR 中包含 Bref 作者 Matthieu Napoli 的实测经验:在测试应用里,AWS Lambda 冷启动减少了 100ms。
max_memory_limit 限制运行时 memory_limit 上限
8.5 以前,代码里执行 ini_set("memory_limit", ...) 没有防护,可以把内存限制改成不健康的高值。8.5 新增系统级 INI max_memory_limit,用来定义运行时 memory_limit 可以被设置到的上限。该改动由 Frederik Pytlick 贡献。
PHP_BUILD_PROVIDER 运行时常量
编译 PHP 时可以通过环境变量 PHP_BUILD_PROVIDER 记录“谁构建了这个版本”,这个信息会显示在 phpinfo() 和 php -v 里。8.5 将其暴露为同名 PHP 常量 PHP_BUILD_PROVIDER,运行时也能读取。Homebrew、Debian、Docker、Fedora 的上游 PHP 构建已经在使用这个标记。
调试
php --ini=diff
新增命令行选项,只列出与默认值不同的 INI 变量。排查 PHP 安装问题或给某个环境报 bug 时,比贴完整 phpinfo() 更直接。
ZEND_MM_DEBUG 运行时内存调试
Arnaud 的 PR 让 PHP 8.5 获得内存调试能力,不需要为了启用 ASAN/MSAN/Valgrind 重新编译。通过设置环境变量 ZEND_MM_DEBUG,即可启用不同的内存调试功能。对生产环境来说,这比要求安装专用工具或维护一个特殊编译版本现实得多。
致命错误默认带 backtrace
Eric Norris 的 RFC 改了错误输出策略:PHP 输出的致命错误现在默认显示 backtrace。此前致命错误只给错误消息,不附带调用栈。
get_exception_handler() 与 get_error_handler()
Arnaud 的 RFC 为 8.5 增加两个函数,分别返回当前已注册的异常处理器和错误处理器对应的 callable 对象。需要包装或临时替换处理逻辑时,可以先把原处理器取出来保存。