如果傳遞過來的變數與別的變數共用一個zval,而且不是引用,則進行強制分離,新的zval的is_ref__gc==0,和 refcount__gc==1. |
| #“|”和”!”将在下文有具体例子讲解,关于”/”虽然大概懂意思,但没想到具体的例子。
走一波与php的交互
正常的样子
在PHP中
<?phpfunction my_function($msg) {
echo "我收到参数啦: {$msg}!\n";
}
my_function('咖啡色的羊驼');
如果my_function写成PHP扩展:
ZEND_FUNCTION(my_function) {
char *msg;
int msg_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",&msg, &msg_len) == FAILURE){
RETURN_NULL();
}
php_printf("我收到参数啦:");
PHPWRITE(msg, msg_len);
php_printf("!\n");
}
两个参数的样子
在PHP中
<?phpfunction my_function($email, $msg) {
echo "我收到参数啦: {$email}、 {$msg}!\n";
}
my_function('123456@qq.com', '咖啡色的羊驼');
如果my_function写成PHP扩展:
ZEND_FUNCTION(my_function) {
char *email;
int email_len;
char *msg;
int msg_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",&msg, &msg_len,&email, &email_len) == FAILURE){
RETURN_NULL();
}
php_printf("我收到参数啦:");
PHPWRITE(email, email_len);
PHPWRITE(msg, msg_len);
php_printf("!\n");
}
两个参数,其中一个可选且有默认值
<?phpfunction my_function($email, $msg = '咖啡色的羊驼') {
echo "我收到参数啦: {$email}、 {$msg}!\n";
}
my_function('123456@qq.com');
如果my_function写成PHP扩展:
ZEND_FUNCTION(my_function) {
char *email;
int email_len;
char *msg = "咖啡色的羊驼";
int msg_len = sizeof("咖啡色的羊驼") - 1;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s",&msg, &msg_len,&email, &email_len) == FAILURE){
RETURN_NULL();
}
php_printf("我收到参数啦:");
PHPWRITE(email, email_len);
PHPWRITE(msg, msg_len);
php_printf("!\n");
}
这里说明了”|”的使用
参数为null时,省内存的写法
先来不省的写法
ZEND_FUNCTION(my_function) {
zval *val;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z",&val) == FAILURE) {
RETURN_NULL();
}
}
省内存
ZEND_FUNCTION(my_function) {
zval *val;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z!",&val) == FAILURE) {
RETURN_NULL();
}
}
这里例子用上了”!”,每个zval,包括IS_NULL型的zval,都需要占用一定的内存空间,需要cpu的计算资源来为它申请内存、初始化,并在它们完成工作后释放掉。但是很多代码都都没有意识到这一点。有很多代码都会把一个null型的值包裹成zval的IS_NULL类型,在扩展开发里这种操作是可以优化的,我们可以把参数接收成C语言里的NULL。
所以就差了一个!,第二个例子就更省了内存。
2.zend_get_arguments()
用法
例子是最好的用法讲解,上例子:
ZEND_FUNCTION(my_function) {
zval *email; if (zend_get_parameters(ZEND_NUM_ARGS(), 1, &email)== FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,"至少需要一个参数");
RETURN_NULL();
}
// ... }
区别与特点
1.能够兼容老版本的PHP,并且只以zval为载体来接收参数。
2.直接获取,而不做解析,不会进行类型转换,所有的参数在扩展实现中的载体都需要是zval类型的。
3.接受失败的时候,不会自己抛出错误,也不能方便的处理有默认值的参数。
4.会自动的把所有符合copy-on-write的zval进行强制分离,生成一个崭新的copy送到函数内部。
综合评价:还是用zend_parse_parameters吧,这个函数了解下即可,不给力。
3.zend_get_parameters_ex()
用法
ZEND_FUNCTION(my_function) {
zval **msg; if (zend_get_parameters_ex(1, &msg) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,"至少需要一个参数");
RETURN_NULL();
}
// ...}
不需要ZEND_NUM_ARGS()作为参数,因为它是在是在后期加入的,那个参数已经不再需要了。
区别与特点
1.此函数基本同zend_get_parameters()。
2.唯一不同的是它不会自动的把所有符合copy-on-write的zval进行强制分离,会用到老的zval的特性
综合评价:极端情况下可能会用到,这个函数了解下即可。
zend_get_parameter_**
这个包括:zend_get_parameters_array_ex()和zend_get_parameters_array()
用法
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<argc; i++) {
php_var_dump(args[i], 1 TSRMLS_CC);
}
efree(args);
}
这个有点复杂,需要解释一下:
程序首先获取参数数量,然后通过safe_emalloc函数申请了相应大小的内存来存放这些zval**类型的参数。这里使用了zend_get_parameters_array_ex()函数来把传递给函数的参数填充到args中。
是的
这个参数专门用于解决像php里面的var_dump的一样,可以无限传参数进去的函数的实现
区别与特点
1.用于应对无限参数的扩展函数的实现。
2.zend_get_parameters_array与zend_get_parameters_array_ex唯一不同的是它将zval*类型的参数填充到args中,并且需要ZEND_NUM_ARGS()作为参数。
综合评价:当遇到确实需要处理无限参数的时候,真的要用这个函数了。zend_parse_parameters真的做不到啊~
总结
抛开一切,最少也要学会zend_parse_parameters()的用法。好的。扩展函数传参数的技能get了。