php擴充與嵌入--php擴充的參數_PHP教程

來源:互聯網
上載者:User
之前的文章中,函數在接收的參數和返回的類型上都比較簡單,但是往往實際中所遇到的都更加複雜一些。這篇文章主要說一下如何在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擴...

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.