深入PHP内核之参数
1、看一下一个扩展中的简单代码
ZEND_BEGIN_ARG_INFO(params_add_arginfo, 0) ZEND_ARG_INFO(0, a) ZEND_ARG_INFO(0, b) ZEND_END_ARG_INFO() PHP_FUNCTION(params_add) { long a,b; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &a, &b) == FALSE) { return; } RETURN_LONG(a+b); }
2、参数相关宏的定义 (Zend/zend_API.h)
#define ZEND_ARG_INFO(pass_by_ref, name)\ { #name, sizeof(#name)-1, NULL, 0, 0, pass_by_ref, 0, 0 }, //声明普通参数,可以用来表示PHP中的int, float, double, string等基本数组类型 #define ZEND_ARG_PASS_INFO(pass_by_ref)\ { NULL, 0, NULL, 0, 0, pass_by_ref, 0, 0 }, //pass_by_ref为1时,强制设置后续的参数为引用类型 #define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null)\ { #name, sizeof(#name)-1, #classname, sizeof(#classname)-1, IS_OBJECT, pass_by_ref, allow_null, 0 }, //声明对象类型的参数 #define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null)\ { #name, sizeof(#name)-1, NULL, 0, IS_ARRAY, pass_by_ref, allow_null, 0 }, //声明数组类型的参数 #define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null)\ { #name, sizeof(#name)-1, NULL, 0, type_hint, pass_by_ref, allow_null, 0 }, #define ZEND_ARG_VARIADIC_INFO(pass_by_ref, name)\ { #name, sizeof(#name)-1, NULL, 0, 0, pass_by_ref, 0, 1 }, #define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args) \ static const zend_arg_info name[] = { \ { NULL, 0, NULL, required_num_args, 0, return_reference, 0, 0 }, #define ZEND_BEGIN_ARG_INFO(name, _unused) \ ZEND_BEGIN_ARG_INFO_EX(name, 0, ZEND_RETURN_VALUE, -1) #define ZEND_END_ARG_INFO() };
3、展开ZEND_BEGIN_ARG_INFO语句
static const zend_arg_info params_add_arginfo[] = { {NULL, 0, NULL, -1, 0, ZEND_RETURN_VALUE, 0}, {a, sizeof(a)-1, NULL, 0, 0, 0, 0}, {b, sizeof(b)-1, NULL, 0, 0, 0, 0}, };
4、参数在zend中的定义(Zend/zend_compile.h)
typedef struct _zend_arg_info { const char *name; //参数的名称 zend_uint name_len; //参数名称的长度 char *class_name; //当参数类型为类时,指定类的名称 zend_uint class_name_len; //类名称的长度 zend_uchar type_hint; zend_bool allow_null; //是否允许设置为null zend_bool pass_by_reference; //是否设置为引用,即使用&操作符 } #define PHP_FUNCTION ZEND_FUNCTION //函数的实现 #define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name)) #define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS) #define INTERNAL_FUNCTION_PARAMETERS \ int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC
5、zend_parse_parameters的参数
Boolean b zend_bool //布尔值 Long l long //长整数 Double d double //双精度浮点数 String s char *, int //符串 (也可能是空字节)和其长度 Resource r zval* // 资源, 保存在 zval* Array a zval* //数组, 保存在 zval* Object o zval* //任何类的)对象, 保存在 zval* zval z zval* //实际的 zval* zval O zval* //由class entry 指定的类的)对象, 保存在 zval* HashTable h HashTable* //数组的哈希表 Function f char *, int //函数,方法名 (版本 > php5.1) | -表明剩下的参数都是可选参数。如果用户没有传进来这些参数值,那么这些值就会被初始化成默认值。 / -表明参数解析函数将会对剩下的参数以 SEPARATE_ZVAL_IF_NOT_REF() 的方式来提供这个参数的一份拷贝,除非这些参数是一个引用。 ! -表明剩下的参数允许被设定为 NULL(仅用在 a、o、O、r和z身上)。如果用户传进来了一个 NULL 值,则存储该参数的变量将会设置为 NULL。
看看官方文档中提供的几个例子:
/* 取得一个长整数,一个字符串和它的长度,再取得一个 zval 值。 */ long l; char *s; int s_len; zval *param; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lsz", &l, &s, &s_len, ¶m) == FAILURE) { return; } /* 取得一个由 my_ce 所指定的类的一个对象,另外再取得一个可选的双精度的浮点数。 */ zval *obj; double d = 0.5; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|d", &obj, my_ce, &d) == FAILURE) { return; } /* 取得一个对象或空值,再取得一个数组。如果传递进来一个空对象,则 obj 将被设置为 NULL。*/ zval *obj; zval *arr; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O!a", &obj, &arr) == FAILURE) { return; } /* 取得一个分离过的数组。*/ zval *arr; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &arr) == FAILURE) { return; } /* 仅取得前 3 个参数(这对可变参数的函数很有用)。*/ zval *z; zend_bool b; zval *r; if (zend_parse_parameters(3, "zbr!", &z, &b, &r) == FAILURE) { return; }
在接收参数时还有一个可用的函数zend_parse_parameters_ex,允许我们传入一些flags来控制解析参数的动作,使用方式如下所示:
int zend_parse_parameters_ex(int flags, int num_args TSRMLS_DC, char *type_spec, ...);
目前flags仅能传入ZEND_PARSE_PARAMS_QUIET这个值,表示函数不输出任何错误信息,如下面的示例:
long l1, l2, l3; char *s; if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "lll", &l1, &l2, &l3) == SUCCESS) { /* manipulate longs */ } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "s", &s, &s_len) == SUCCESS) { /* manipulate string */ } else { php_error(E_WARNING, "%s() takes either three long values or a string as argument", get_active_function_name(TSRMLS_C)); return; }
可变参数
由于PHP支持可变参数,所以在接收可变参数时,使用前面介绍的两个方法就不太合适,我们可以用zend_get_parameters_array_ex()来代替,如下面的示例:
zval **parameter_array[4]; /* 取得参数个数 */ argument_count = ZEND_NUM_ARGS(); /* 看一下参数个数是否满足我们的要求:最少 2 个,最多 4个。 */ if(argument_count < 2 || argument_count > 4) { WRONG_PARAM_COUNT; } /* 参数个数正确,开始接收。 */ if(zend_get_parameters_array_ex(argument_count, parameter_array) != SUCCESS) { WRONG_PARAM_COUNT; }
6、获取参数数量
PHP无法根据函数的显式声明来对调用进行语法检查,而且它还支持可变参数,所以我们就不得不在所调用函数的内部来获取参数个数。我们可以使用宏ZEND_NUM_ARGS来获取参数个数,如下面的代码:
if(ZEND_NUM_ARGS() != 2) { WRONG_PARAM_COUNT }
这段代码使用宏WRONG_PARAM_COUNT抛出一个参数个数错误
7、更多的参数类型
#define Z_TYPE_P //获取参数类型 //IS_NULL|IS_BOOL|IS_LONG|IS_DOUBLE|IS_STRING|IS_ARRAY|IS_RESOURCE|IS_OBJECT #define Z_BVAL_P //获取bool类型参数的值 #define Z_LVAL_P //获取long类型参数的值 #define Z_DVAL_P //获取float类型参数的值 #define Z_STRVAL_P //获取string类型参数的值 #define Z_STRLEN_P //获取string类型参数的长度 #define Z_ARRVAL_P //获取array类型参数的值 #define Z_RESVAL_P //获取resource类型参数的值 #define Z_OBJCE_P //获取object类型参数的值
- 作者:踏雪无痕
- 出处:http://www.cnblogs.com/chenpingzhao/
- 本文版权归作者和博客园共有,如需转载,请联系 pingzhao1990#163.com