PHP怎样在扩展中操作变量_PHP在扩展中操作变量接口【接口】

7次阅读

PHP怎样在扩展中操作变量_PHP在扩展中操作变量接口【接口】

zval 是什么,为什么不能直接用 C 变量

php 扩展里操作变量,本质是操作 zval 结构体。它不是 C 的 intchar*,而是带类型、引用计数、GC 标记的复合体。直接读写 zval->value.lval 会崩溃或内存泄漏——因为没处理类型校验、引用分离、生命周期。

  • 传入参数是字符串但你当整数取 zval->value.lval → 读到垃圾值
  • 修改一个被多个变量共享的 zval(比如函数参数是引用传递)→ 影响调用方,必须先 SEPARATE_ZVAL_NOREF
  • 返回新变量却忘了调用 RETVAL_STRINGRETURN_LONG → PHP 层收不到值,甚至 segfault

获取用户传入参数:用 zend_parse_parameters 而不是手动解包

手动从 execute_data->thisexecute_data->func->op_array->num_args 里扒参数,既错又慢。PHP 提供了类型安全的解析宏,自动完成类型转换NULL 检查、引用分离。

  • 要读一个必需的整数:zend_parse_parameters(ZEND_NUM_ARGS(), "l", &my_long)
  • 可选字符串 + 默认值:zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &str, &str_len)s! 表示允许 NULL)
  • 数组参数必须用 a,拿到的是 zval*,别用 char* 接 —— 否则 PHP 7.4+ 会直接报错 Segmentation fault (core dumped)
  • 如果参数个数不对或类型不匹配,zend_parse_parameters 自动抛 Warning 并返回 FAILURE,必须检查返回值

构造并返回变量:用 RETVAL_* 宏,别碰 return_value 指针裸操作

return_value 是函数调用上的 zval*,它的内存由 Zend VM 管理。直接赋值 return_value->value.lval = 42 很危险:类型没设、refcount 没增、可能指向只读内存。

  • 返回整数:RETVAL_LONG(123)(自动设 type = IS_LONG,refcount = 1)
  • 返回字符串(需复制):RETVAL_STRING("hello")(内部调用 estrndup,你不用 free)
  • 返回已存在的 zval(比如从数组里取出来):RETVAL_ZVAL(src_zval, 1, 0) —— 第二个参数 1 表示 copy,第三个 0 表示不 dtor src
  • 千万别写 ZVAL_LONG(return_value, 42) 后就 return;它不设置 type 标志位,PHP 层看到的是未定义类型,行为不可预测

修改全局变量或超全局数组:小心作用域和持久化

想在扩展里改 $_GET$GLOBALS['foo']?得走 Zend API,不能用 zend_hash_find 直接改哈希桶。

立即学习PHP免费学习笔记(深入)”;

  • $_SERVERzend_is_auto_global_str("_SERVER", sizeof("_SERVER") - 1) 先确认它是 auto global,再用 zend_hash_str_update 更新其内部 zval
  • 设全局变量:zend_set_local_var_str("myvar", sizeof("myvar")-1, &val, 1)(第四个参数 1 表示 copy)
  • 在 MINIT 阶段注册的全局变量,若用 EG(scope)CG(active_op_array) 错误上下文访问,会 crash —— 因为那时还没执行到脚本作用域
  • 所有写入超全局的操作,都应避免在 RINIT 前或 RSHUTDOWN 后进行,否则 zval 可能已被销毁

最常被忽略的一点:zval 的类型转换不是隐式的。比如把 IS_STRING 强转成 long,得调用 convert_to_long(),而不是直接取 zval->value.lval —— 否则 PHP 8 会触发 ZEND_ASSERT 失败。

text=ZqhQzanResources