搜索
首页后端开发php教程php扩展之扩展函数的传参以及获取参数的实例详解

前言

经过上一文 php扩展之扩展框架的自动生成 ,对php扩展框架的整体了解,基本上可以说,对于扯淡如何写php扩展和关键点有了一定的把握,但关键的还是在于如何写PHP_FUNCTION的函数。
本文主要记录一下,php在调用扩展的时候进行传参,那么扩展函数是怎么接招的。当作自己的备忘录

正文

1.zend_parse_parameters

获取函数传递的参数,可以使用zend_parse_parameters函数,细心的同学会发现官方生成的默认的函数也是用这个函数来接收参数的。

这个函数怎么用?

首先可以把这个看书当作php的scanf一样使用。(这个函数不熟悉的自觉这里)

zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, &参数1,&参数2…);

第一个参数是传递给函数的参数个数。通常的做法是传给它ZEND_NUM_ARGS()。(ZEND_NUM_ARGS() 来表示对传入的参数“有多少要多少”)这是一个表示传递给函数参数总个数的宏。

第二个参数是为了线程安全,总是传递TSRMLS_CC宏。

第三个参数是一个字符串,指定了函数期望的参数类型,后面紧跟着需要随参数值更新的变量列表。由于PHP是弱类型语言,采用松散的变量定义和动态的类型判断,而c语言是强类型的,而zend_parse_parameters()就是为了把不同类型的参数转化为期望的类型。(当实际的值无法强制转成期望的类型的时候,会发出一个警告)

第四第五直到第n个参数,都是要传进来的值的数值。

第三个参数详解

关于第三个参数,这里提供一个供选择的参数列表:

类型指定符 对应的C类型 描述
l long 符号整数
d double 浮点数
s char *, int 二进制字符串,长度
b zend_bool 逻辑型(1或0)
r zval * 资源(文件指针,数据库连接等)
a zval * 联合数组
o zval * 任何类型的对象
O zval * 指定类型的对象。需要提供目标对象的类类型
z zval * 无任何操作的zval

这边有两点需要特别注意
1.这里的字符串类型对应的c类型有两个参数。是的。这说明在使用zend_parse_parameters()函数的时候需要这么使用:

zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",&name, &name_len)

一个表示字符串内容,另一个是字符串长度。

2.需要大概知道一下,zval是啥?
zval是Zend引擎的值容器。无论这个变量是布尔型,字符串型或者其他任何类型,其信息总会包含在一个zval联合体中。来一波zval的结构:

typedef union _zval {    long lval;    double dval;    struct {        char *val;        int len;

    } str;

    HashTable *ht;

    zend_object_value obj;

} zval;

ps:关于typedef就不解释了,因为我发现有人已经写的非常好了,请点击关于typedef的用法总结

这边还涉及到额外的三个用法,来增强我们接收参数的能力:

符号 解释
| 它之前的参数都是必须的,之后的都是非必须的,也就是有默认值的。
! 如果接收了一个PHP语言里的null变量,则直接把其转成C语言里的NULL,而不是封装成IS_NULL类型的zval。
/ 如果传递过来的变量与别的变量共用一个zval,而且不是引用,则进行强制分离,新的zval的is_ref__gc==0, and refcount__gc==1.

“|”和”!”将在下文有具体例子讲解,关于”/”虽然大概懂意思,但没想到具体的例子。

走一波与php的交互

正常的样子

在PHP中

<?phpfunction my_function($msg) {
    echo "我收到参数啦: {$msg}!\n";
}
my_function(&#39;咖啡色的羊驼&#39;);

如果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(&#39;123456@qq.com&#39;, &#39;咖啡色的羊驼&#39;);

如果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 = &#39;咖啡色的羊驼&#39;) {
    echo "我收到参数啦: {$email}、 {$msg}!\n";
}
my_function(&#39;123456@qq.com&#39;);

如果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了。

以上是php扩展之扩展函数的传参以及获取参数的实例详解的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
php怎么把负数转为正整数php怎么把负数转为正整数Apr 19, 2022 pm 08:59 PM

php把负数转为正整数的方法:1、使用abs()函数将负数转为正数,使用intval()函数对正数取整,转为正整数,语法“intval(abs($number))”;2、利用“~”位运算符将负数取反加一,语法“~$number + 1”。

php怎么实现几秒后执行一个函数php怎么实现几秒后执行一个函数Apr 24, 2022 pm 01:12 PM

实现方法:1、使用“sleep(延迟秒数)”语句,可延迟执行函数若干秒;2、使用“time_nanosleep(延迟秒数,延迟纳秒数)”语句,可延迟执行函数若干秒和纳秒;3、使用“time_sleep_until(time()+7)”语句。

php字符串有没有下标php字符串有没有下标Apr 24, 2022 am 11:49 AM

php字符串有下标。在PHP中,下标不仅可以应用于数组和对象,还可应用于字符串,利用字符串的下标和中括号“[]”可以访问指定索引位置的字符,并对该字符进行读写,语法“字符串名[下标值]”;字符串的下标值(索引值)只能是整数类型,起始值为0。

php怎么除以100保留两位小数php怎么除以100保留两位小数Apr 22, 2022 pm 06:23 PM

php除以100保留两位小数的方法:1、利用“/”运算符进行除法运算,语法“数值 / 100”;2、使用“number_format(除法结果, 2)”或“sprintf("%.2f",除法结果)”语句进行四舍五入的处理值,并保留两位小数。

php怎么读取字符串后几个字符php怎么读取字符串后几个字符Apr 22, 2022 pm 08:31 PM

在php中,可以使用substr()函数来读取字符串后几个字符,只需要将该函数的第二个参数设置为负值,第三个参数省略即可;语法为“substr(字符串,-n)”,表示读取从字符串结尾处向前数第n个字符开始,直到字符串结尾的全部字符。

php怎么根据年月日判断是一年的第几天php怎么根据年月日判断是一年的第几天Apr 22, 2022 pm 05:02 PM

判断方法:1、使用“strtotime("年-月-日")”语句将给定的年月日转换为时间戳格式;2、用“date("z",时间戳)+1”语句计算指定时间戳是一年的第几天。date()返回的天数是从0开始计算的,因此真实天数需要在此基础上加1。

php怎么替换nbsp空格符php怎么替换nbsp空格符Apr 24, 2022 pm 02:55 PM

方法:1、用“str_replace("&nbsp;","其他字符",$str)”语句,可将nbsp符替换为其他字符;2、用“preg_replace("/(\s|\&nbsp\;||\xc2\xa0)/","其他字符",$str)”语句。

php怎么查找字符串是第几位php怎么查找字符串是第几位Apr 22, 2022 pm 06:48 PM

查找方法:1、用strpos(),语法“strpos("字符串值","查找子串")+1”;2、用stripos(),语法“strpos("字符串值","查找子串")+1”。因为字符串是从0开始计数的,因此两个函数获取的位置需要进行加1处理。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
3 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能

安全考试浏览器

安全考试浏览器

Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

螳螂BT

螳螂BT

Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。