代码 PHP C 扩展移植到 PHP 8.6:UPGRADING.INTERNALS 里的内部 API 移除与替换

2026-09-23 21:02:57

PHP C 扩展移植到 PHP 8.6:UPGRADING.INTERNALS 里的内部 API 移除与替换

维护 PHP C 扩展时,把源码移植到 8.6 需要处理的核心问题是:8.6 移除或改签了一批内部 API,旧代码会直接编译失败,或者编译通过但行为出错。php-src 仓库根目录的 UPGRADING.INTERNALS 是这份清单的权威来源,它按「破坏性变更、移除、修改、新增」四类列出 8.6 的内部 API 变化,并逐项给出替换写法。

适配路径:准备 8.6 开发构建环境 → 对照移除清单逐项修改扩展代码 → 重新构建 → make test 验证。

动手前:定位调用点与构建验证

在改代码之前,先按符号名在扩展源码里检索一遍,把调用点列全,再对照 UPGRADING.INTERNALS 分类处理,避免漏改某个分支里的调用。扩展侧的常规构建验证流程:

phpize
./configure --enable-your-ext
make -j"$(nproc)"
make test

函数名、宏名、参数类型、结构字段类型这几类变化会在编译阶段暴露出来;make test 则用来覆盖运行期路径,确认改动没有改变原有行为。

移除项与替换写法

移除项8.6 的替换写法原因
ZVAL_IS_NULL()使用 Z_ISNULL()原命名有误(misnamed)
下划线前缀的流函数(_php_stream_cast()_php_stream_free()_php_stream_seek()_php_stream_tell()_php_stream_read()_php_stream_write() 等 29 个)去掉前缀的宏现在是正式函数名,直接调用原为宏/函数双形态
zend_save_error_handling()移除
zend_parse_parameter()使用某个 zend_parse_arg_TYPE() API

几处需要注意的细节:

  • ZVAL_IS_NULL() 属于命名问题,替换为 Z_ISNULL() 后语义不变。
  • 29 个下划线前缀的流函数整体去掉了前缀,无下划线的名字现在是正式函数名,原来写在扩展里的 _php_stream_* 调用需要逐个改名。
  • zend_save_error_handling() 没有替换项,调用点要连同它周围的错误处理逻辑一起删掉。
  • zend_parse_parameter() 改用对应的 zend_parse_arg_TYPE() 系列 API,具体用哪个取决于原来解析的参数类型。

改名与结构变化

  • ZEND_AST_METHOD_REFERENCE 改名为 ZEND_AST_TRAIT_METHOD_REFERENCE。AST 节点枚举名变更,按节点类型做分派的代码要同步改。
  • ZSTR_INIT_LITERAL()zend_string_starts_with_literal()zend_string_starts_with_literal_ci() 现在支持含 NUL 字节的字符串,但不再接受非字面量 char*:传入的必须是字面量,运行期拼出来的 char* 不再被接受。
  • zend_function.arg_info 现在总是 zend_arg_info*;此前内部函数是 zend_internal_arg_info(除非设置了 ZEND_ACC_USER_ARG_INFO)。读取 arg_info 时不再需要按函数类型分支处理。
  • EG(in_autoload) 改名为 EG(autoload_current_classnames),且从指针变为直接内嵌的 HashTable 结构,访问方式要跟着调整。
  • zend_argument_error_variadic() 新增 'function' 参数;php_verror() 移除了 param 参数。
  • php_stream_wrapper_log_error() 签名从 (wrapper, options, fmt, ...) 变为 (wrapper, context, options, severity, terminating, code, fmt, ...)。要保持旧行为的参数取值为:contextNULL、severity 传 E_WARNING、code 传 ZEND_ENUM_StreamErrorCode_Genericterminating 仅当错误会中止操作时传 true
  • zend_create_closure()zend_create_fake_closure()zend_create_partial_closure() 现在接收绑定对象为 zend_object*(原来是 zval*);相应地 zend_get_closure_this_ptr() 返回 zend_object*,未绑定时为 NULL(原来是 IS_UNDEFzval*)。拿到返回值后的判空条件要一并改。

模块内部符号

Removed/Changed 只覆盖核心内部 API。如果扩展依赖 date、intl、mbstring、mysqlnd、session、standard、uri、xml 等内置模块的内部符号,第 3 节 “Module changes” 里有各自的改动,需要单独核对。

例如 session 的 PS_OPEN_ARGS 现在定义为 void **mod_data, zend_string *save_path, zend_string *session_name_php_error_log() 改为接收 zend_string* 且带 zend_result 返回类型。这类改动分散在各模块名下,只跑一遍核心清单很容易漏掉。

新增 API

8.6 同时新增了一批接口:zend_hash_str_lookup()zend_bin2hex() / zend_bin2hex_str()zval_try_get_double()zend_ast_dup(),以及 中的 php_io_copy() 等。替换旧实现时,在 “New” 一节确认新接口的准确名称和用途,再决定是否把手写的等价逻辑换掉。

复制全文 生成海报 PHP 扩展 C 版本

推荐文章

程序员茄子在线接单