PHP_FUNCTION(set_time_limit) { long new_timeout; char *new_timeout_str; int new_timeout_strlen; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &new_timeout) == FAILURE) { return; } new_timeout_strlen = zend_spprintf(&new_timeout_str, 0, "%ld", new_timeout); if (zend_alter_ini_entry_ex("max_execution_time", sizeof("max_execution_time"), new_timeout_str, new_timeout_str len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC) == SUCCESS) { RETVAL_TRUE; } else { RETVAL_FALSE; } efree(new_timeout_str); }
這是一個PHP函數 set_time_limit,zend_parse_parameters()函數的前幾個參數咱們直接用內核裏宏來生成的
ZEND_NUM_ARGS() TSRMLS_CC 注意二者之間有個空格,可是沒有逗號。ZEND_NUM_ARGS()表明着參數的個數。 php
緊接着須要傳遞給zend_parse_parameters()函數的參數是一個用於格式化的字符串,就像printf的第一個參數同樣
type_spec是格式化字符串,其常見的含義以下:
參數表明着的類型數組
b Boolean l Integer 整型 d Floating point 浮點型 s String 字符串 r Resource 資源 a Array 數組 o Object instance 對象 O Object instance of a specified type 特定類型的對象 z Non-specific zval 任意類型~ Z zval**類型 f 表示函數、方法名稱,PHP5.3以前沒有的
這個函數就像printf()函數同樣,後面的參數是與格式化字符串裏的格式一一對應的。一些基礎類型的數據會直接映射成C語言裏的類型。
socket
參數對應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**
下面的一些字符在類型說明字符串(就是那個 char *type_spec)中具備特別的含義:
| - 代表剩下的參數都是可選參數。若是用戶沒有傳進來這些參數值,那麼這些值就會被初始化成默認值。 / - 代表參數解析函數將會對剩下的參數以 SEPARATE_ZVAL_IF_NOT_REF() 的方式來提供這個參數的一份拷貝,除非這些參數是一個引用。 ! - 代表剩下的參數容許被設定爲 NULL(僅用在 a、o、O、r和z身上)。若是用戶傳進來了一個 NULL 值,則存儲該參數的變量將會設置爲 NULL。
固然啦,熟悉這個函數的最好的方法就是舉個例子來講明。下面咱們就來看一個例子:
/* 取得一個長整數,一個字符串和它的長度,再取得一個 zval 值 */ long l; char *s; int s_len; zval *param; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"lsz", &l, &s, &s_len, ¶m) == FAILURE) { return; } /* 取得一個由 my_ce 所指定的類的一個對象,另外再取得一個可選的雙精度的浮點數 */ zval *obj; double d = 0.5; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"O|d", &obj, my_ce, &d) == FAILURE) { return; } /* 取得一個對象或空值,再取得一個數組 若是傳遞進來一個空對象,則 obj 將被設置爲 NULL */ zval *obj; zval *arr; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O!a", &obj, &arr) == FAILURE) { return; } /* 取得一個分離過的數組 */ zval *arr; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &arr) == FAILURE) { return; } /* 僅取得前 3 個參數(這對可變參數的函數頗有用) */ zval *z; zend_bool b; zval *r; if (zend_parse_parameters(3, "zbr!", &z, &b, &r) == FAILURE) { return; }
注意,在最後的一個例子中,咱們直接用了數值 3 而不是 ZEND_NUM_ARGS() 來做爲想要取得參數的個數。
這個參數解析函數還有一個帶有附加標誌的擴展版本,這個標誌可讓你控制解析函數的某些動做。函數
int zend_parse_parameters_ex(int flags, int num_args TSRMLS_DC, char *type_spec, ...);
這個標誌(flags)目前僅接受 ZEND_PARSE_PARAMS_QUIET 這一個值,它表示這個函數不輸出任何錯誤信息。這對那些能夠傳入徹底不一樣類型參數的函數很是有用,但這樣你也就不得不本身輸出錯誤信息spa
下面就是一個如何既能夠接收 3 個長整形數又能夠接收一個字符串的例子:指針
long l1, l2, l3; char *s; if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, 「lll」, &l1, &l2, &l3) == SUCCESS) { /* manipulate longs */ } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), 「s」, &s, &s_len) == SUCCESS) { /* manipulate string */ } else { php_error(E_WARNING, 「%s() takes either three long values or a string as argument」, get_active_function_name(TSRMLS_C)); return; }
我想你經過上面的那些例子就能夠基本掌握如何接收和處理參數了。若是你想看更多的例子,請翻閱 PHP 源碼包中那些自帶的擴展的源代碼,那裏麪包含了你可能遇到的各類狀況。code
獲取函數參數這件事情咱們還能夠經過 zend_get_parameters_ex() 來完成(不推薦使用這些舊式的 API,咱們推薦您使用前面所述的新式的參數解析函數):對象
zval **parameter; if(zend_get_parameters_ex(1, ¶meter) != SUCCESS) WRONG_PARAM_COUNT;
全部的參數都存儲在一個二次指向的 zval 容器裏面(其實就是一個 zval* 數組,譯者注)。上面的這段代碼嘗試接收 1 個參數而且將其保存在 parameter 所指向的位置。blog
zend_get_parameters_ex() 至少須要兩個參數。第一個參數表示咱們想要接收參數的個數(這個值一般是對應於 PHP 函數參數的個數,由此也能夠看出事先對調用語法正確性的檢查是多麼重要)。第二個參數(包括剩下的全部參數)指向一個二次指向 zval 的指針。(即 ***zval,是否是有點糊塗了?^_^)這些指針是必須的,由於 Zend 內部是使用 **zval 進行工做的。爲了能被在咱們函數內部定義的 **zval 局部變量所訪問,咱們就必須在用一個指針來指向它。three
zend_get_parameters_ex() 的返回值能夠是 SUCCESS 或 FAILURE,分別表示參數處理的成功或失敗。若是處理失敗,那最大的可能就是因爲沒有指定一個正確的參數個數。若是處理失敗,則應該使用宏 WRONG_PARAM_COUNT 來退出函數。
若是想接收更多的的參數,能夠用相似下面一段的代碼來處理:
zval **param1, **param2, **param3, **param4; if(zend_get_parameters_ex(4, ¶m1, ¶m2, ¶m3, ¶m4) != SUCCESS) WRONG_PARAM_COUNT;
zend_get_parameters_ex() 僅檢查你是否在試圖訪問過多的參數。若是函數有 5 個參數,而你僅僅接收了其中的 3 個,那麼你將不會收到任何錯誤信息,zend_get_parameters_ex() 僅返回前三個參數的值。再次調用 zend_get_parameters_ex() 也不會得到剩下兩個參數的值,而仍是返回前三個參數的值。
若是你想接收一些可變參數,那用前面咱們剛剛討論的方法就不太合適了,主要是由於咱們將不得不爲每一個可能的參數個數來逐行調用 zend_get_parameters_ex(),顯然這很不爽。
爲了解決這個問題,咱們能夠借用一下 zend_get_parameters_array_ex() 這個函數。它能夠幫助咱們接收不定量的參數並將其保存在咱們指定的地方:
zval **parameter_array[4]; /* 取得參數個數 */ argument_count = ZEND_NUM_ARGS(); /* 看一下參數個數是否知足咱們的要求:最少 2 個,最多 4個。 */ if(argument_count < 2 || argument_count > 4) WRONG_PARAM_COUNT; /* 參數個數正確,開始接收。 */ if(zend_get_parameters_array_ex(argument_count, parameter_array) != SUCCESS) WRONG_PARAM_COUNT;
讓咱們來看看這幾行代碼。首先代碼檢查了傳入參數的個數,確保在咱們可接受的範圍內;而後就調用 zend_get_parameters_array_ex() 把全部有效參數值的指針填入 parameter_array。
咱們能夠在 fsockopen() 函數(位於ext/standard/fsock.c )中找到一個更爲漂亮的實現。代碼大體以下,你也不用擔憂尚未弄懂所有的函數,由於咱們很快就會談到它們。
pval **args[5]; int *sock=emalloc(sizeof(int)); int *sockp; int arg_count=ARG_COUNT(ht); int socketd = -1; unsigned char udp = 0; struct timeval timeout = { 60, 0 }; unsigned short portno; unsigned long conv; char *key = NULL; FLS_FETCH(); if (arg_count > 5 || arg_count < 2 || zend_get_parameters_array_ex(arg_count,args)==FAILURE) { CLOSE_SOCK(1); WRONG_PARAM_COUNT; } switch(arg_count) { case 5: convert_to_double_ex(args[4]); conv = (unsigned long) (Z_DVAL_PP(args[4]) * 1000000.0); timeout.tv_sec = conv / 1000000; timeout.tv_usec = conv % 1000000; /* fall-through */ case 4: if (!PZVAL_IS_REF(*args[3])) { php_error(E_WARNING,」error string argument to fsockopen not passed by reference」); } pval_copy_constructor(*args[3]); ZVAL_EMPTY_STRING(*args[3]); /* fall-through */ case 3: if (!PZVAL_IS_REF(*args[2])) { php_error(E_WARNING,」error argument to fsockopen not passed by reference」); return; } ZVAL_LONG(*args[2], 0); break; } convert_to_string_ex(args[0]); convert_to_long_ex(args[1]); portno = (unsigned short) Z_LVAL_P(args[1]); key = emalloc(Z_STRLEN_P(args[0]) + 10);
fsockopen() 能夠接收 2-5 個參數。在必需的變量聲明以後便開始檢查參數的數量範圍。而後在一個 switch 語句中使用了貫穿(fall-through)法來處理這些的參數。這個 switch 語句首先處理最大的參數個數(即 5),隨後依次處理了參數個數爲 4 和 3 的狀況,最後用 break 關鍵字跳出 switch 來忽略對其餘狀況下參數(也就是隻含有 2 個參數狀況)的處理。這樣在通過 switch 處理以後,就開始處理參數個數爲最小時(即 2)的狀況。
這種像樓梯同樣的多級處理方法能夠幫助咱們很方便地處理一些可變參數。
爲 了存取一些參數,讓每一個參數都具備一個明確的(C)類型是頗有必要的。但 PHP是一種動態語言,PHP 從不作任何類型檢查方面的工做,所以無論你想不想,調用者均可能會把任何類型的數據傳到你的函數裏。好比說,若是你想接收一個整數,但調用者卻可能會給你 傳遞個數組,反之亦然-PHP 可無論這些的。
爲了不這些問題,你就必須用一大套 API 函數來對傳入的每個參數都作一下強制性的類型轉換。(見表3.4 參數類型轉換函數)
注意: 全部的參數轉換函數都以一個 **zval 來做爲參數。