编程 PHP 8.5.10 升级要点:深层嵌套不再栈溢出、PDO_ODBC NULL 修正、UTF-8 正则禁用 \C

2026-09-07 21:03:22

PHP 8.5.10 升级要点:深层嵌套不再栈溢出、PDO_ODBC NULL 垃圾值修正、UTF-8 正则禁用 \C

PHP 8.5.10(2026-08-27)是 8.5 分支的 bugfix 版本,改动集中在递归/深层嵌套导致的崩溃、PDO 驱动取值正确性、非 UTF-8 字符串处理、Opcache JIT 以及一批释放后使用问题。以下按升级时容易直接踩中的模块整理,GH 编号可到 php.net changelog 对应 issue 核对。

深层嵌套:数组比较、递归数组函数、DOM 规范化

这次最集中的一类修复,是在深层嵌套数据上执行递归操作导致的栈溢出。

  • Core GH-23088:深层嵌套数组比较栈溢出
  • Standard GH-23111:array_walk_recursive() 处理深层嵌套数组栈溢出
  • Standard GH-23113:array_replace_recursive() 同样问题
  • Standard GH-23115:compact() 遇深层嵌套数组栈溢出
  • DOM GH-23116:DOMDocument::normalize() 栈溢出
  • DOM GH-23117:Dom\XMLDocument::normalize() 栈溢出
  • DOM GH-23120:DOMNode::isEqualNode() 比较深层节点栈溢出

外部 JSON、XML、YAML 反序列化后如果嵌套层数不可控,并且后续有比较、递归遍历或 DOM 规范化操作,升级前建议重点回归。

同版 DOM 还修了一个不那么显眼的问题:GH-22825。当 DTD 给属性声明了默认值时,DOMElement::setAttribute() 会静默失败,现在能正确设置并反映到节点上。

正则与多字节字符串:\C 行为变更、偏移量错误

  • PCRE GH-21134:\C 配合 UTF-8 模式会崩溃,PHP 8.5.10 起禁止在 UTF-8 pattern 中使用 \C
  • MBString GH-22779:mb_strrpos() 在非 UTF-8 编码下使用负偏移量时返回错误位置
  • MBString GH-21036:mb_eregi() 使正则缓存失效后,再调 mb_ereg_search_getregs() 会崩溃

\C 的改动是这版里少数属于行为变更的修复:

// 旧版在特定输入下可能崩溃(GH-21134)
preg_match('/\C/u', $input);

// 8.5.10 起:UTF-8 pattern 中不再允许 \C,需要按字符语义改写

代码里还有 /u\C 组合的话,升级后会从“偶发崩溃”变成“编译/运行时报错”,需要提前清掉。

非 UTF-8 环境下使用 mb_strrpos() 且传入负 offset 的代码,返回结果会变化,建议对着固定用例做断言回归。

PDO:ODBC 长列 NULL 与 PgSQL lazy fetch

PDO_ODBC

GH-23016:ODBC 长列中的 NULL 值会以垃圾二进制串形式返回,8.5.10 修正为返回 NULL。受影响的是读取 LONG/VARCHAR 等长列且允许为 NULL 的查询。如果之前为绕开垃圾值写过类型判断或字符串过滤,升级后可以简化。

PDO_PGSQL

PDO_PGSQL 修了一批 PDO::ATTR_PREFETCH => 0(lazy fetch)下的缺陷,集中在语句生命周期管理:

  • 清理一个遗留在 COPY 过程中的 fetch 时可能无限循环
  • 使用 emulated prepares 或禁用 prepares 时销毁 statement 会触发 use-after-free
  • fetch 结束后连接仍处于 busy 状态,影响下一次 fetch
  • 某些场景下会从 statement 取出另一个 statement 接管结果后的行

启用 lazy fetch 并且涉及 PostgreSQL COPY、自定义 prepare 开关的代码,升级后要重点跑语句销毁与结果集切换路径。

Opcache JIT

JIT 修了 4 个代码生成问题:

  • GH-22693:x86_64 上 JIT 生成的 TLS 访问出现 DT_TEXTREL
  • GH-22763:设置寄存器后未清除 ZREG_TYPE_ONLY,导致类型状态残留
  • GH-22857:带 property hook getter 的 FETCH_OBJ_FUNC_ARG 生成错误代码,丢失寄存器持有的变量
  • GH-22916:zend_jit_deoptimizer_start() 未保留父寄存器

这些基本是“错误代码生成 → 随机崩溃/错误结果”一类。生产环境开启 JIT 的,升级后先跑属性 hook 和函数调用密集的回归。

释放后使用与生命周期问题

  • Core GH-22782:preloading 下常量表达式中的 first-class callable(如 strlen(...))崩溃
  • Streams GH-15836:用户流过滤器在 close flush 期间访问 $this->stream 触发 use-after-free
  • XSL:DOMDocument 子类的 __clone() 保留了 XSLTProcessor::importStylesheet() 制作的样式表副本,导致 use-after-free
  • Date:重复调用 DatePeriod::__construct() 造成泄漏
  • Session:mod_mm 存储损坏;GH-23043 中错误的 session id 会破坏 zend_mm_heap
  • SQLite:blob stream 仍打开时关闭数据库会泄漏
  • OpenSSL:无效 ALPN 协议列表缺少错误检查
  • Exif:exif_read_data() 会按超出源文件大小的 HEIF meta box 分配缓冲区
  • Intl:IntlListFormatter::__construct() 成功调用后仍残留旧的全局错误状态
  • Sockets:socket_set_option()UDP_SEGMENTSO_LINGER 的校验错误信息修正,另有若干内存问题修复

自定义 stream filter、DOMDocument 子类 clone、preload 常量表达式属于低频但崩溃特征明显的路径,升级后值得单独回归。

Reflection 与 SimpleXML 边界行为

Reflection 三个修复:

  • GH-22905:反射异常消息在 null 字节处截断,现可输出完整消息
  • ReflectionProperty::isLazy()skipLazyInitialization():子类 hook 继承属性时错误使用父类槽位
  • ReflectionMethod::createFromMethodName() 在不可实例化子类上段错误

依赖反射做框架初始化、属性懒加载(lazy object)的代码,注意子类继承属性时的行为变化。

SimpleXML 两个修复:

  • 无法解析的整数元素偏移现在不会错误别名到已有元素
  • 比较未初始化的 SimpleXMLElement 实例不再段错误

升级与自查点

这版 API 层面没有迁移项,但以下调用路径的行为有实际变化,建议在 CI 里补一轮回归:

  • 深层嵌套数组的 == 比较、array_walk_recursive()array_replace_recursive()compact()
  • DOM 的 normalize()isEqualNode()、带 DTD 默认属性时的 setAttribute()
  • 正则中 /u\C 的组合
  • 非 UTF-8 编码下 mb_strrpos() 的负偏移量
  • mb_eregi()mb_ereg_search_getregs() 混用的代码
  • PDO_ODBC 长列 NULL 取值
  • PDO_PGSQL 的 lazy fetch、COPY、prepares 开关组合
  • 开启 JIT 后涉及 property hook getter 的调用
  • 自定义用户流过滤器的 close flush 路径
  • XSLTProcessor::importStylesheet() 配合 DOMDocument 子类 clone 的场景
  • Reflection 异常消息、lazy 属性在子类 hook 继承属性时的表现

改动集中在“以前崩溃、现在正常”和“返回值修正”两类。行为变化的范围明确(主要是 \C 禁用),可以按常规 bugfix 版本节奏合入。

复制全文 生成海报 PHP 8.5 Opcache PDO PCRE

推荐文章

程序员茄子在线接单