php7引⽤传递,7.6.3引⽤传参
### 7.6.3 引⽤传参
上⼀节介绍了如何在内部函数中解析参数,这⾥还有⼀种情况没有讲到,那就是引⽤传参:
```php
$a = array();
function my_func(&$a){
php8兼容php7吗$a[] = 1;
}
```
上⾯这个例⼦在函数中对$a的修改将反映到原变量上,那么这种⽤法如何在内部函数中实现呢?上⼀节介绍参数解析的过程中并没有提到⽤户函数中参数的zend_arg_info结构,内部函数中也有类似的⼀个结构⽤于函数注册时指定参数的⼀些信息:zend_internal_arg_info。
```c
typedef struct _zend_internal_arg_info {
const char *name; //参数名
const char *class_name;
zend_uchar type_hint; //显式声明的类型
zend_uchar pass_by_reference; //是否引⽤传参
zend_bool allow_null; //是否允许参数为NULL,类似"!"的⽤法
zend_bool is_variadic; //是否为可变参数
} zend_internal_arg_info;
```
这个结构⼏乎与zend_arg_info完全⼀样,不同的地⽅只在于name、class_name的类型,zend_arg_info这两个成员的类型都是
zend_string。如果函数需要使⽤引⽤类型的参数或返回引⽤就需要创建函数的参数数组,这个数组通过:`ZEND_BEGIN_ARG_INFO()或ZEND_BEGIN_ARG_INFO_EX()`、`ZEND_END_ARG_INFO()`宏定义:
```c
#define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args)
#define ZEND_BEGIN_ARG_INFO(name, _unused)
```
* __name:__ 参数数组名,注册函数`PHP_FE(function, arg_info)`会⽤到
* ___unused:__ 保留值,暂时⽆⽤
* __return_reference:__ 返回值是否为引⽤,⼀般很少会⽤到
* __required_num_args:__ required参数数
这两个宏需要与`ZEND_END_ARG_INFO()`配合使⽤:
```c
ZEND_BEGIN_ARG_INFO_EX(arginfo_my_func_1, 0, 0, 2)
ZEND_END_ARG_INFO()
```
接着就是在上⾯两个宏中间定义每⼀个参数的zend_internal_arg_info,PHP提供的宏有:
```c
//pass_by_ref表⽰是否引⽤传参,name为参数名称
#define ZEND_ARG_INFO(pass_by_ref, name) { #name, NULL, 0, pass_by_ref, 0, 0 },
//只声明此参数为引⽤传参
#define ZEND_ARG_PASS_INFO(pass_by_ref) { NULL, NULL, 0, pass_by_ref, 0, 0 },
//显式声明此参数的类型为指定类的对象,等价于PHP中这样声明:MyClass $obj
#define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, #classname, IS_OBJECT, pass_by_ref, allow_null, 0 },
//显式声明此参数类型为数组,等价于:array $arr
#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) { #name, NULL, IS_ARRAY, pass_by_ref, allow_null, 0 },
//显式声明为callable,将检查函数、成员⽅法是否可调
#define ZEND_ARG_CALLABLE_INFO(pass_by_ref, name, allow_null) { #name, NULL, IS_CALLABLE, pass_by_ref, allow_null, 0 },
//通⽤宏,⾃定义各个字段
#define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, NULL, type_hint, pass_by_ref, allow_null, 0 },
//声明为可变参数
#define ZEND_ARG_VARIADIC_INFO(pass_by_ref, name) { #name, NULL, 0, pass_by_ref, 0, 1 },
```
举个例⼦来看:
```php
function my_func_1(&$a, Exception $c){
...
}
```
⽤内核实现则可以这么定义:
```c
ZEND_BEGIN_ARG_INFO_EX(arginfo_my_func_1, 0, 0, 1)
ZEND_ARG_INFO(1, a) //引⽤
ZEND_ARG_OBJ_INFO(0, b, Exception, 0) //注意:这⾥不要把字符串加""
ZEND_END_ARG_INFO()
展开后:
```c
static const zend_internal_arg_info name[] = {
//多出来的这个是给返回值⽤的
{ (const char*)(zend_uintptr_t)(2), NULL, 0, 0, 0, 0 },
{ "a", NULL, 0, 0, 0, 0 },
{ "b", "Exception", 8, 1, 0, 0 },
}
```
第⼀个数组元素⽤于记录必传参数的数量以及返回值是否为引⽤。定义完这个数组接下来就需要把这个数组告诉函数:
```c
const zend_function_entry mytest_functions[] = {
PHP_FE(my_func_1, arginfo_my_func_1)
PHP_FE(my_func_2, NULL)
PHP_FE_END //末尾必须加这个
};
```
引⽤参数通过`zend_parse_parameters()`解析时只能使⽤"z"解析,不能再直接解析为zend_value了,否则引⽤将失效:```c
PHP_FUNCTION(my_func_1)
{
zval *lval; //必须为zval,定义为zend_long也能解析出,但不是引⽤
zval *obj;
if(zend_parse_parameters(ZEND_NUM_ARGS(), "zo", &lval, &obj) == FAILURE){
RETURN_FALSE;
}
//lval的类型为IS_REFERENCE
zval *real_val = Z_REFVAL_P(lval); //获取实际引⽤的zval地址:&(lval.value->ref.val)
Z_LVAL_P(real_val) = 100; //设置实际引⽤的类型
}
```
```php
$a = 90;
$b = new Exception;
my_func_1($a, $b);
echo $a;
==========[output]===========
100
```
> __Note:__ 参数数组与zend_parse_parameters()有很多功能重合,两者都会⽣效,对zend_internal_arg_info验证在
zend_parse_parameters()之前,为避免混乱两者应该保持⼀致;另外,虽然内部函数的参数数组并不强制定义声明,但还是建议声明。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论