PHP7内部函数的参数是如何解析的

由于使用yaf框架Yaf_Response_Http::getHeader没有相关的文档只能查看源码确认。通过源码可以看出有一个可选参数。

PHP内核函数使用zend_parse_parameters()将实际参数的值接收到C变量中。
此函数使用scanf()方法进行参数定义。
由包含说明符列表的字符串定义的所需数据的数量和类型(“s” - 表示字符串,“l”表示长整数等)。
不幸的是,每次php调用此函数时都必须解析此字符串,这会产生显着的性能开销。
在php7之后的版对此优化,使用新API fast_zpp宏 提高性能。

在php7可以使用zend_parse_parameters、fast_zpp两种方式获取php参数,以下是内部函数获取参数源码。

/** {{{ proto public Yaf_Response_Http::getHeader(void)
*/
PHP_METHOD(yaf_response_http, getHeader) {

	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S", &name) == FAILURE) {
		return;
	}
ZEND_FUNCTION(define)
{

	ZEND_PARSE_PARAMETERS_START(2, 3)
		Z_PARAM_STR(name)
		Z_PARAM_ZVAL(val)
		Z_PARAM_OPTIONAL
		Z_PARAM_BOOL(non_cs)
	ZEND_PARSE_PARAMETERS_END();

第一个代码片断取自鸟哥yaf框架源码
https://github.com/laruence/yaf/blob/931dc56c7a53c388c21d679ae10dfe0b245aed32/responses/yaf_response_http.c
第二个代码片断取处php7.4 defin函数源码
https://github.com/php/php-src/blob/master/Zend/zend_builtin_functions.c

使用旧API获取参数

zend_parse_parameters()

这包括了两个操作: ⼀个是取参数的个数, ⼀个是解析参数列表。
取参数的个数

取参数的个数是通过ZEND_NUM_ARGS()宏来实现的。 其定义如下:

#define ZEND_NUM_ARGS() (ht)

解析参数列表
源码可在zend_API.h查看

PHP内部函数在解析参数时使⽤的是 zend_parse_parameters。 它可以⼤⼤简化参数的接收处理⼯作, 虽然它在处理可变参数时还有点弱。
其声明如下:

ZEND_API int zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, ...)
  • 第⼀个参数num_args表明表示想要接收的参数个数, 我们经常使⽤ZEND_NUM_ARGS() 来表示对传⼊的参数“有多少要多少”。
  • 第⼆参数应该总是宏 TSRMLS_CC 。
  • 第三个参数 type_spec 是⼀个字符串, ⽤来指定我们所期待接收的各个参数的类型, 有点类似于printf 中指定输出格式的那个格式化字串。
  • 剩下的参数就是我们⽤来接收PHP参数值的变量的指针。

zend_parse_parameters() 在解析参数的同时会尽可能地转换参数类型, 这样就可以确保我们总是能得到所期望的类型的变量。 任何⼀种标量类型都可以转换为另外⼀种标量类型, 但是不能在标量类型与复杂类型(⽐如数组、 对象和资源等) 之间进⾏转换。 如果成功地解析和接收到了参数并且在转换期间也没出现错误, 那么这个函数就会返回 SUCCESS, 否则返回 FAILURE。 如果这个函数不能接收到所预期的参数个数或者不能成功转换参数类型时就会抛出⼀些错误信息。
第三个参数指定的各个参数类型列表如下所示:

  • l - ⻓整形
  • d - 双精度浮点类型
  • s - 字符串 (也可能是空字节)和其⻓度
  • b - 布尔型
  • r - 资源, 保存在 zval*
  • a - 数组, 保存在 zval*
  • o - (任何类的) 对象, 保存在 zval *
  • O - (由class entry 指定的类的) 对象, 保存在 zval *
  • z - 实际的 zval*
    除了各个参数类型, 第三个参数还可以包含下⾯⼀些字符, 它们的含义如下:
  • | - 表明剩下的参数都是可选参数。 如果⽤户没有传进来这些参数值, 那么这些值就会被初始化成默认值。
  • / - 表明参数解析函数将会对剩下的参数以 SEPARATE_ZVAL_IF_NOT_REF() 的⽅式来提供这个参数的⼀份拷⻉, 除⾮这些参数是⼀个引⽤。
  • ! - 表明剩下的参数允许被设定为 NULL(仅⽤在 a、 o、 O、 r和z身上) 。 如果⽤户传进来了⼀个 NULL 值, 则存储该参数的变量将会设置为 NULL

使用新API获取参数

ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args)

两个参数,分别是必传参数和最多可选参数。

Z_PARAM_STR(zend_string *dest)
Z_PARAM_STRING(char *dest, size_t *dest_len)
Z_PARAM_ZVAL(zval *dest)
Z_PARAM_LONG(zend_long dest)
Z_PARAM_ARRAY(zval *dest)
Z_PARAM_RESOURCE(zval *dest)
Z_PARAM_OPTIONAL

常用获取参数方法

END_PARSE_PARAMETERS_END()

结束获取参数
新API涵盖了旧API的所有功能。
新API与旧API对照表

specifie Fast ZPP API macro args
| Z_PARAM_OPTIONAL
a Z_PARAM_ARRAY(dest) dest - zval*
A Z_PARAM_ARRAY_OR_OBJECT(dest) dest - zval*
b Z_PARAM_BOOL(dest) dest - zend_bool
C Z_PARAM_CLASS(dest) dest - zend_class_entry*
d Z_PARAM_DOUBLE(dest) dest - double
f Z_PARAM_FUNC(fci, fcc) fci - zend_fcall_info, fcc - zend_fcall_info_cache
h Z_PARAM_ARRAY_HT(dest) dest - HashTable*
H Z_PARAM_ARRAY_OR_OBJECT_HT(dest) dest - HashTable*
l Z_PARAM_LONG(dest) dest - long
L Z_PARAM_STRICT_LONG(dest) dest - long
o Z_PARAM_OBJECT(dest) dest - zval*
O Z_PARAM_OBJECT_OF_CLASS(dest, ce) dest - char*, dest_len - int
p Z_PARAM_PATH(dest, dest_len) dest - zval*
P Z_PARAM_PATH_STR(dest) dest - zend_string*
r Z_PARAM_RESOURCE(dest) dest - zval*
s Z_PARAM_STRING(dest, dest_len) dest - char*, dest_len - int
S Z_PARAM_STR(dest) dest - zend_string*
z Z_PARAM_ZVAL(dest) dest - zval*
Z_PARAM_ZVAL_DEREF(dest) dest - zval*
+ Z_PARAM_VARIADIC(’+’, dest, num) dest - zval*, num int
* Z_PARAM_VARIADIC(’*’, dest, num) dest - zval*, num int
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章