您好,欢迎访问一九零五行业门户网

php扩展与嵌入--php扩展的参数_PHP教程

之前的文章中,函数在接收的参数和返回的类型上都比较简单,但是往往实际中所遇到的都更加复杂一些。这篇文章主要说一下如何在php扩展开发中接收来自于用户空间的参数,并且对这些参数的类型、个数等信息进行相应的检查。
1. 使用zend_parse_parameters()进行自动的类型转换
在php的扩展中,最容易的得到输入参数的方法就是使用zend_parse_parameters()函数。
对这个函数的调用的第一个参数总是:zend_num_args() tsrmls_cc. 这个参数返回一个int型的输入参数的数目。
第二个参数是format参数,是由字符串类型组成,分别对应着不同的zend engine支持的类型。
下图中给出了format参数可能具有的类型:
而接下来的参数取决于之前所请求的类型。对于比较简单的类型来说,这个参数一般都是取引用的基元,如下例所示:php_function(sample_getlong){ long foo; if (zend_parse_parameters(zend_num_args() tsrmls_cc, l, &foo) == failure) { return_null(); } php_printf(the integer value of the parameter you passed is: %ld\n, foo); return_true;}
这里是l也就是long类型,所以相对应的提前声明了一个long foo参数,然后通过引用的方式把值传递了进来。下面给出更加详细的参数与c语言中的类型的对应关系:b ------ zend_booll ------- longd ------- doubles ------- char* , intr ------- zval*a ------ zval*o ------ zval*o ----- zval*, zend_class_entry*z ------ zval*z ----- zval**
注意到对于复杂的类型采用的是简单的zval*类型。这跟返回复杂类型的时候没有return_*的道理是一样的。zpp所做的事情是保证所接收到的zval*是正确的类型。如果必要的话,它也会执行隐式的转换,比如把数组转成stdclass对象。
对于s类型来说,它比较特殊,一个char*一个int,这个还是主要因为php里面字符串的特殊结构:
function sample_hello_world($name) { echo hello $name!\n;}
在c语言中,要使用的就是zend_parse_parameters函数了:php_function(sample_hello_world){ char *name; int name_len; if (zend_parse_parameters(zend_num_args() tsrmls_cc, s, &name, &name_len) == failure) { return_null(); } php_printf(hello ); phpwrite(name, name_len); php_printf(!\n);}
如果有多个参数的话,那么zend_parse_parameters会从左到右去提取这些参数:function sample_hello_world($name, $greeting) { echo hello $greeting $name!\n;}sample_hello_world('john smith', 'mr.');
or:
php_function(sample_hello_world){ char *name; int name_len; char *greeting; int greeting_len; if (zend_parse_parameters(zend_num_args() tsrmls_cc, ss, &name, &name_len, &greeting, &greeting_len) == failure) { return_null(); } php_printf(hello ); phpwrite(greeting, greeting_len); php_printf( ); phpwrite(name, name_len); php_printf(!\n);}
除了类型标识符之外,还有三个元字符来修改参数被处理的方式:| :如果看到它了,说明前面的参数都是必须的,后面的参数都是可选的! :如果接收了一个php语言中的null变量,则直接转成c语言里的null,而不是封装成is_null类型的zval
/ :如果传递过来的变量与别的变量共用一个zval,而且不是真引用,那就要强制分离,新zval的is_ref__gc = 0,refcount__gc = 1可选的参数:php中可以给参数提供默认值:
function sample_hello_world($name, $greeting='mr./ms.') { echo hello $greeting $name!\n;}
这个时候在调用的时候,可以不提供第二个参数:sample_hello_world('ginger rogers','ms.');sample_hello_world('fred astaire');
在c的解释中,有类似的实现方式:php_function(sample_hello_world){ char *name; int name_len; char *greeting = mr./mrs.; int greeting_len = sizeof(mr./mrs.) - 1;//给定默认值,找出默认的长度 if (zend_parse_parameters(zend_num_args() tsrmls_cc, s|s, &name, &name_len, &greeting, &greeting_len) == failure) {//特殊的元字符|立刻就用上了 return_null(); } php_printf(hello ); phpwrite(greeting, greeting_len); php_printf( ); phpwrite(name, name_len); php_printf(!\n);}
对于可选参数来说,除非指定一般是不会有值的,所以提供默认参数很重要。大部分的情况下是null/0
is_null vs null:
每一个zval的类型,即使是最简单的is_null类型,都占据一定的内存空间,同时也需要时间去申请和释放它们。所以很多时候没有必要使用这个类型,下面两段代码里面给出了对比:
php_function(sample_arg_fullnull){ zval *val; if (zend_parse_parameters(zend_num_args() tsrmls_cc, z, &val) == failure) { return_null(); } if (z_type_p(val) == is_null) {//使用zval检查为空的方式 val = php_sample_make_defaultval(tsrmls_c); }...php_function(sample_arg_nullok){ zval *val; if (zend_parse_parameters(zend_num_args() tsrmls_cc, z!, &val) == failure) { return_null(); } if (!val) {// c语言风格的检查为空的方式 val = php_sample_make_defaultval(tsrmls_c); }...
forced seperation强制分离:
当一个变量传入函数的时候,不管是不是用传引用的方式,refcount总是至少为2.一个是本身,一个是传进函数的拷贝。在对这个zval进行更改之前,把它从一个非引用的集合中分离出来是很必要的。
使用/会很方便,它会自动的把任何copy-on-write引用(也就是假引用的)的变量分离出来。
这个特性跟null标志位一样,需要的时候才用到。
zend_get_parameters():
如果想要兼容老版本的php或只想以zval作为载体来接收参数,那么可以考虑使用zend_get_parameters()函数来接收参数
它与zend_parse_parameters()相比,直接获取,不做解析。不会自动进行类型转换,所有参数在扩展实现中的载体都是用zval的.
zend_function(sample_onearg) {
zval *firstarg; if (zend_get_parameters(zend_num_args(), 1, &firstarg)== failure) { php_error_docref(null tsrmls_cc, e_warning,expected at least 1 parameter.); return_null(); } /* do something with firstarg... */}
同时,它在接收失败的时候不会自己抛出错误,也不能处理具有默认值的参数,最后一点跟parse不同的地方在于它会自动的把所有符合copy-on-write的zval进行强制分离,生成一个崭新的拷贝送到函数内部。
如果不需要这个功能可以用zend_get_parameters_ex()它的参数是zval**的
zend_function(sample_onearg) { zval **firstarg; if (zend_get_parameters_ex(1, &firstarg) == failure) { wrong_param_count;抛出一个e_warning级别的错误信息,并自动return。 } /*
可变参数,处理任意数目的参数:
还有两种zend_get_parameters_**函数,专门用来解决很多或者无法提前知道参数数目的情况。php语言中的var_dump()函数,可以输入任意数量的参数。
zend_function(var_dump) { int i, argc = zend_num_args(); zval ***args; args = (zval ***)safe_emalloc(argc, sizeof(zval **), 0); if (zend_num_args() == 0 || zend_get_parameters_array_ex(argc, args) == failure) { efree(args); wrong_param_count; } for (i=0; i
程序首先获取参数数量,然后通过safe_emalloc函数申请相应大小的内存来存储这些zval**的参数。这里使用zend_get_parameters_array_ex()函数来把传递给函数的参数填充到args中。提醒一下,还存在一个zend_get_parameters_array()函数,唯一不同是它将zval*类型的参数填充到args中,并且需要zend_num_args()作为参数。
2. arg info参数和类型的绑定
这个arg info结构是ze2才有的。每一个arg info声明都由一个zend_begin_arg_info()或zend_begin_arg_info_ex()宏组成,后面跟着0个或多个zend_arg_*info(), 然后最后以zend_end_arg_info()作为结尾。
假定要重写count()函数:
php_function(sample_count_array){ zval *arr; if (zend_parse_parameters(zend_num_args() tsrmls_cc, a, &arr) == failure) { return_null(); } return_long(zend_hash_num_elements(z_arrval_p(arr)));}
zend_parse_parameters()会确保输入到你函数中的参数是一个数组。但是如果你需要用zend_get_parameter()的话,那么就需要自己在函数内部内建类型检查。除非使用类型绑定:
zend_begin_arg_info(php_sample_array_arginfo, 0) zend_arg_array_info(0, arr, 0) zend_end_arg_info()。。。 php_fe(sample_count_array, php_sample_array_arginfo) 。。。
通过这种方式,zend engine就会帮你进行类型检查了。同时还给了参数一个名字,从而使得产生的错误信息更加具有可读性。
而对于对象来说,也可以通过arg info进行限定:
zend_begin_arg_info(php_sample_class_arginfo, 0) zend_arg_object_info(1, obj, stdclass, 0) zend_end_arg_info()
这里第一个参数被设为1,表示是引用方式传递,但是对象其实在ze2中都是引用传递的。不要忘记了array和object的allow_null选项。
如果使用的是php4的话,只能用php_type_p()进行检查,或使用convert_to_type()方法进行类型转换。
http://www.bkjia.com/phpjc/635059.htmlwww.bkjia.comtruehttp://www.bkjia.com/phpjc/635059.htmltecharticle之前的文章中,函数在接收的参数和返回的类型上都比较简单,但是往往实际中所遇到的都更加复杂一些。这篇文章主要说一下如何在php扩...
其它类似信息

推荐信息