深入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

浙公网安备 33010602011771号