函数参数解析
之前我们定义的函数没有接收任何参数,那么扩展定义的内部函数如何读取参数呢?用户自定义函数在编译时会为每个参数创建一个zend_arg_info结构,这个结构用来记录参数的名称、是否引用传参、是否为可变参数等,在存储上函数参数与局部变量相同,都分配在zend_execute_data上,且最先分配的就是函数参数,调用函数时首先会进行参数传递,按参数次序依次将参数的value从调用空间传递到被调函数的zend_execute_data,函数内部像访问普通局部变量一样通过存储位置访问参数,这是用户自定义函数的参数实现。
/* arg_info for user functions */
typedef struct _zend_arg_info {
zend_string *name;//参数名
zend_string *class_name;
zend_uchar type_hint;//显式声明的参数类型,比如(array $param_1)
zend_uchar pass_by_reference;//是否引用传参,参数前加&的这个值就是1
zend_bool allow_null;//是否允许为NULL,注意:这个值并不是用来表示参数是否为必传的
zend_bool is_variadic;//是否为可变参数,即...用法,与golang的用法相同,5.6以上新增的一个用法:function my_func($a, ...$b){...}
} zend_arg_info;
PHP中通过 zend_parse_parameters() 这个函数解析zend_execute_data上保存的参数:
zend_parse_parameters(int num_args, const char *type_spec, ...);
- num_args为实际传参数,通过 ZEND_NUM_ARGS()获取;
- type_spec是一个字符串,用来标识解析参数的类型,比如:"la"表示第一个参数为整形,第二个为数组,将按照这个解析到指定变量;
- 后面是一个可变参数,用来指定解析到的变量,这个值与type_spec配合使用,即type_spec用来指定解析的变量类型,可变参数用来指定要解析到的变量,这个值必须是指针。
解析的过程也比较容易理解,调用函数时首先会把参数拷贝到调用函数的zend_execute_data上,所以解析的过程就是按照type_spec指定的各个类型,依次从zend_execute_data上获取参数,然后将参数地址赋给目标变量。
参数类型
整形:l、L
整形通过"l"或"L"标识,表示解析的参数为整形,解析到的变量类型必须是 zend_long ,不能解析其它类型,如果输入的参数不是整形将按照类型转换规则将其转为整形:
zend_long lval;
if(zend_parse_parameters(ZEND_NUM_ARGS(), "l", &lval){
...
}
printf("lval:%d\n", lval);
如果在标识符后加"!",即:"l!"、"L!",则必须再提供一个zend_bool变量的地址,通过这个值可以判断传入的参数是否为NULL,如果为NULL则将要解析到的zend_long值设置为0,同时zend_bool设置为1:
zend_long lval; //如果参数为NULL则此值被设为0
zend_bool is_null;//如果参数为NULL则此值为1,否则为0
if(zend_parse_parameters(ZEND_NUM_ARGS(), "l!", &lval, &is_null){
..
}
布尔型:b
通过"b"标识符表示将传入的参数解析为布尔型,解析到的变量必须是zend_bool:
zend_bool ok;
if(zend_p