首页 后端开发 php教程 php扩展与嵌入--php扩展的参数_PHP教程

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

Jul 13, 2016 am 10:42 AM
嵌入 扩展

之前的文章中,函数在接收的参数和返回的类型上都比较简单,但是往往实际中所遇到的都更加复杂一些。这篇文章主要说一下如何在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_bool l ------- long d ------- double s ------- char* , int r ------- 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<br>
程序首先获取参数数量,然后通过safe_emalloc函数申请相应大小的内存来存储这些zval**的参数。这里使用zend_get_parameters_array_ex()函数来把传递给函数的参数填充到args中。提醒一下,还存在一个zend_get_parameters_array()函数,唯一不同是它将zval*类型的参数填充到args中,并且需要ZEND_NUM_ARGS()作为参数。<br>
</p>
<p><br>
</p>
<p><br>
</p>
<p><strong>2. Arg info参数和类型的绑定</strong></p>
<p>这个arg info结构是ZE2才有的。每一个arg info声明都由一个ZEND_BEGIN_ARG_INFO()或ZEND_BEGIN_ARG_INFO_EX()宏组成,后面跟着0个或多个ZEND_ARG_*INFO(), 然后最后以ZEND_END_ARG_INFO()作为结尾。<br>
假定要重写count()函数:<br>
</p>
<p><pre class="code">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()方法进行类型转换。









www.bkjia.comtruehttp://www.bkjia.com/PHPjc/635059.htmlTechArticle之前的文章中,函数在接收的参数和返回的类型上都比较简单,但是往往实际中所遇到的都更加复杂一些。这篇文章主要说一下如何在php扩...
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热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 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

php如何使用PHP的SNMP扩展? php如何使用PHP的SNMP扩展? Jun 02, 2023 am 10:22 AM

PHP的SNMP扩展是一种使PHP能够通过SNMP协议与网络设备进行通信的扩展程序。使用该扩展可以方便地获取和修改网络设备的配置信息,例如路由器、交换机等设备的CPU、内存、网络接口等信息,也可以进行诸如开关设备端口等控制操作。本文将介绍SNMP协议的基础知识、PHP的SNMP扩展的安装方法以及如何在PHP中使用SNMP扩展进行网络设备的监控和控制。一、SN

PHP与HTML结合:三种嵌入代码的技巧 PHP与HTML结合:三种嵌入代码的技巧 Mar 06, 2024 am 08:09 AM

PHP与HTML结合是网页开发中常见的一种技术,通过PHP可以在HTML文件中嵌入动态内容、实现辅助功能等,大大提高了网站的交互性和可定制性。本文将介绍三种嵌入代码的技巧,并提供具体的代码示例供参考。一、使用PHP标记嵌入代码最常见的方式是使用PHP标记()将PHP代码嵌入到HTML文件中,实现动态内容的显示。例如,可以使用PHP

从头到尾:如何使用php扩展cURL进行HTTP请求 从头到尾:如何使用php扩展cURL进行HTTP请求 Jul 29, 2023 pm 05:07 PM

从头到尾:如何使用php扩展cURL进行HTTP请求引言:在Web开发中,经常需要与第三方API或其他远程服务器进行通信。而使用cURL进行HTTP请求是一种常见而强大的方式。本文将介绍如何使用php扩展cURL来执行HTTP请求,并提供一些实用的代码示例。一、准备工作首先,确保php已安装cURL扩展。可以在命令行执行php-m|grepcurl查

PHP 函数的扩展和第三方模块 PHP 函数的扩展和第三方模块 Apr 13, 2024 pm 02:12 PM

要扩展PHP函数功能,可以使用扩展和第三方模块。扩展提供附加函数和类,可通过pecl包管理器安装和启用。第三方模块提供特定功能,可通过Composer包管理器安装。实践案例包括使用扩展解析复杂JSON数据和使用模块验证数据。

CENTOS7下如何安装mbstring扩展? CENTOS7下如何安装mbstring扩展? Jan 06, 2024 pm 09:59 PM

1.UncaughtError:Calltoundefinedfunctionmb_strlen();出现如上错误时,说明我们没装上mbstring扩展;2.进入PHP安装目录cd/temp001/php-7.1.0/ext/mbstring3.启动phpize(/usr/local/bin/phpize或/usr/local/php7-abel001/bin/phpize)命令来安装php扩展4../configure--with-php-config=/usr/local/php7-abel

如何使用极光推送扩展,在PHP应用中实现批量消息推送功能 如何使用极光推送扩展,在PHP应用中实现批量消息推送功能 Jul 25, 2023 pm 08:07 PM

如何使用极光推送扩展,在PHP应用中实现批量消息推送功能在移动应用的开发中,消息推送是一项非常重要的功能。极光推送是一种常用的消息推送服务,提供了丰富的功能和接口。本文将介绍如何使用极光推送扩展在PHP应用中实现批量消息推送功能。第一步:注册极光推送账号并获取API密钥首先,我们需要在极光推送官网(https://www.jiguang.cn/push)注册

php如何使用PHP的ZipArchive扩展? php如何使用PHP的ZipArchive扩展? Jun 02, 2023 am 08:13 AM

PHP是一种流行的服务器端语言,可以用来开发Web应用程序和处理文件。PHP的ZipArchive扩展是一个强大的工具,可以在PHP中操作zip文件。在这篇文章中,我们将介绍如何使用PHP的ZipArchive扩展来创建、读取和修改zip文件。一、安装ZipArchive扩展在使用ZipArchive扩展之前,需要确保已经安装了这个扩展。安装方法如下:1.安

php如何使用PHP的POSIX扩展? php如何使用PHP的POSIX扩展? Jun 03, 2023 am 08:01 AM

PHP的POSIX扩展是一组允许PHP与POSIX兼容操作系统进行交互的函数和常量。POSIX(PortableOperatingSystemInterface)是一组操作系统接口标准,旨在允许软件开发人员编写可在各种UNIX或UNIX类操作系统上运行的应用程序。本文将介绍如何使用PHP的POSIX扩展,包括安装和使用。一、安装PHP的POSIX扩展在

See all articles