zend engine 之 zend_parse_parameters()函数详解

 我们来看一下以下代码片段: PHP_FUNCTION是一个宏, 这是PHP扩展C++函数的一种写法(另一种写法是PHP_METHOD(funcname), 在定义类的成员函数的时候用到). 它在php.h中定义如下: 而ZEND_FUNCTION又是一个宏, 它在zend_API.h中定义如下: 这个宏里面又有两个宏: ZEND_NAMED_FUNCTION和ZEND_FN.ZEND_NAMED_FUNCTION在zend_API.h中定义如下: ZEND_FE在zend_API.h中定义如下: 经过上述四个宏展开以后, PHP_FUNCTION(lychee_cli_usr_get_balance)变成以下形式: 当然这还不是最终的形式, 因为INTERNAL_FUNCTION_PARAMETERS还是一个宏, 它在zend.h中定义如下: 所以PHP_FUNCTION(lychee_cli_usr_get_balance)最终的被展开的形式是: 这里涉及到一个zend engine的一个重要的数据类型: zval, 这里先不解释它是什么了. 以后慢慢再讲.

我们来看看上面那几个参数什么意思:

ParameterDescription
htThe number of arguments passed to the Zend function. You should not touch this directly, but instead use ZEND_NUM_ARGS() to obtain the value.
return_valueThis variable is used to pass any return values of your function back to PHP. Access to this variable is best done using the predefined macros. For a description of these see below.
this_ptrUsing this variable, you can gain access to the object in which your function is contained, if it's used within an object. Use the functiongetThis() to obtain this pointer.
return_value_used

This flag indicates whether an eventual return value from this function will actually be used by the calling script. 0 indicates that the return value is not used; 1 indicates that the caller expects a return value. Evaluation of this flag can be done to verify correct usage of the function as well as speed optimizations in case returning a value requires expensive operations (for an example, see how array.cmakes use of this).

 

上述代码片段定义了一个函数名为: lychee_cli_usr_get_balance()的函数, 它需要4个参数: 第一个参数的类型是字符串型, 第二个参数是长整型, 第三个参数是长整型, 第四个参数是字符串型.函数 zend_parse_parameters() 的作用是获取在PHP文件调用函数 lychee_cli_usr_get_balance()时传入的参数. 但是它是怎么获得这四个参数的呢? 请看下面:
在zend_API.c的1132行中可以找到如下定义:
 该函数的第一个参数: num_args, 是一个int类型, 表示参数的个数. 在实际调用的时候,你看到它是一个宏: ZEND_NUM_ARGS(), 这个宏在zend_API.h中定义如下:  事实上你也可以传入一个数值, 但这个数必须和你实际传入的参数个数一致, 不然, 在你调用lychee_cli_usr_get_balance()的时候会报错, 而且报错信息还不是正确的信息. 以上面的为例: 你传入的第一个参数必须为4, 如果不是为4, 而是为5, 当你在调用lychee_cli_usr_get_balance()时会报这样的错: "lychee_cli_usr_get_balance()需要4个参数, 但是传入了5个", 而无论实际上你传入了多少个参数, 它都会报个这错误. 所以这里推荐使用ZEND_NUM_ARGS() 这个宏, 在不正确调用的时候, 会报正确的错误. 为什么呢? 这时候RETURN_IF_ZERO_ARGS()函数出场啦. 见名思义, 当参数个数为0的时候返回. RETURN_IF_ZERO_ARGS是一个宏, 它在zend_API.c中定义如下:  必须同时满足(type_spec)[0] == 0 && __num_args != 0 && !(quiet), 然后才返回FAILURE, 就是说获取参数失败, 而失败的信息会经过zend_error()函数打印出来. 后面两个条件很容易满足, 最重要的是第一个条件. 第一个条件才符合该宏的名字RETURN_IF_ZERO_ARGS. 这里还有两个函数: get_active_class_name()以及get_active_function_name(). 这两个函数会捕捉到PHP页面调用的当前的类名和函数名.
1139 va_start(va, type_spec);         // 初始化变量va, 然后给下面的函数调用
1140 retval = zend_parse_va_args(num_args, type_spec, &va, 0 TSRMLS_CC);   // 这行代码根据参数的个数, 以及 类型指定符来匹配参数是否正确. 如果正确, 便为参数分配存储空间;如果错误, 会打印出错误信息. zend_parse_va_args()函数在zend_API.c中定义如下:  我们先看923行的for循环: 它扫描type_spec, 遇到正确的类型指定符或类型修饰符"|", 统计出实际传入的参数个数, 以及最小的需要传入的参数个数; 如果遇到不正确的类型指定符, 便打印出类型不匹配的错误信息 
982行代码: 如果没有遇到"|", 那么便设置最小需要传入的参数个数为扫描到的实际参数个数.
992--1005行代码: 如果扫描到的实际参数个数和传入的参数个数不相等, 那么便打印出参数个数不对的错误信息.
1051--1100行代码: 如果代码执行到这里, 那么表示, 传入的参数正确, 并且参数的类型也正确, 那么就要为这些参数分配存储空间了. 而1068--1081行代码正确做了这些事情.
该函数的第二个参数: TSRMLS_DC, 是一个宏, 在线程安全的时候用到的.
 
该函数的第三个参数: type_spec 是一个字符串类型, 表示类型指定符. 其具体信息如下:
Type Specifier(类型指定符)      Userspace Datatype(用户空间数据类型)
b                              Boolean
l                              Integer
d                              Floating point
s                              String
r                              Resource
a                              Array
o                              Object instance
O                              Object instance of a specified type
z                              Non-specific zval
Z                              Dereferenced non-specific zval
特殊的Type Sepcifier还包括: "|", "!", "/"这三个东东. 这三个东东是干嘛使的呢? 请看下面:
Type Modifier    Meaning
|                Optional parameters follow. When this is specified, all previous parameters are considered required and all subsequent parameters are considered optional.
例如: "ss|s", 表示前面两个"ss"是必须要传入的, 而后面的那个"s"是可选的.
!                If a NULL is passed for the parameter corresponding to the preceding argument specifier, the internal variable provided will be set to an actual NULL pointer as opposed to an IS_NULL zval.
例如: "z!", 表示该修饰符之前的参数可以被设置为空. 如果传入的参数为空, 则把该参数设置成一个空的指针, 而不是设置成一个空的zval值. 网上有文章说此修饰符的类型必须为zval*类型的变量. 也许是以前的版本, 但现在新的版本可以不为zval*. 
/                If the parameter corresponding to the preceding argument specifier is in a copy-on-write reference set, it will be automatically separated into a new zval with is_ref==0, and refcount==1.
该函数的最后一些参数: (...), 它表示该函数接受变参. 这些参数就是你实际调用lychee_cli_usr_get_balance()时的形参. 必须在前面加上"&", 否则会报错. 而在传递字符串参数时, 该参数后面必须还得加上一个整形参数, 表示该字符串的长度, 否则也会报错.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值