首頁 php教程 php手册 php扩展与嵌入--php扩展的参数

php扩展与嵌入--php扩展的参数

Jun 13, 2016 am 09:46 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()方法进行类型转换。









本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡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脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++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擴充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的SNMP擴充? php如何使用PHP的SNMP擴充? Jun 02, 2023 am 10:22 AM

PHP的SNMP擴充是一種讓PHP能夠透過SNMP協定與網路設備進行通訊的擴充功能。使用此擴展可以方便地獲取和修改網路設備的配置信息,例如路由器、交換器等設備的CPU、內存、網絡接口等信息,也可以進行諸如開關設備端口等控制操作。本文將介紹SNMP協定的基礎知識、PHP的SNMP擴充的安裝方法以及如何在PHP中使用SNMP擴充進行網路設備的監控與控制。一、SN

PHP 函數的擴充和第三方模組 PHP 函數的擴充和第三方模組 Apr 13, 2024 pm 02:12 PM

若要擴充PHP函數功能,可以使用擴充和第三方模組。擴充功能提供附加函數和類,可透過pecl套件管理器安裝和啟用。第三方模組提供特定功能,可透過Composer套件管理器安裝。實作案例包括使用擴充解析複雜JSON資料和使用模組驗證資料。

PHP與HTML結合:三種嵌入程式碼的技巧 PHP與HTML結合:三種嵌入程式碼的技巧 Mar 06, 2024 am 08:09 AM

PHP與HTML結合是網頁開發中常見的技術,透過PHP可以在HTML文件中嵌入動態內容、實現輔助功能等,大大提高了網站的互動性和可自訂性。本文將介紹三種嵌入程式碼的技巧,並提供具體的程式碼範例供參考。一、使用PHP標記嵌入程式碼最常見的方式是使用PHP標記()將PHP程式碼嵌入HTML檔案中,實現動態內容的顯示。例如,可以使用PHP

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的ZipArchive擴充? php如何使用PHP的ZipArchive擴充? Jun 02, 2023 am 08:13 AM

PHP是一種流行的伺服器端語言,可以用來開發網頁應用程式和處理檔案。 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擴充在

如何使用極光推播擴展,在PHP應用中實現大量訊息推播功能 如何使用極光推播擴展,在PHP應用中實現大量訊息推播功能 Jul 25, 2023 pm 08:07 PM

如何使用極光推播擴展,在PHP應用中實現大量訊息推播功能在行動應用的開發中,訊息推播是一項非常重要的功能。極光推送是一種常用的訊息推播服務,提供了豐富的功能和介面。本文將介紹如何使用極光推播擴充功能在PHP應用中實現大量訊息推播功能。第一步:註冊極光推播帳號並取得API金鑰首先,我們需要在極光推播官網(https://www.jiguang.cn/push)註冊

See all articles