深刻PHP內核之參數

一、看一下一個擴展中的簡單代碼 php

ZEND_BEGIN_ARG_INFO(params_add_arginfo, 0)
    ZEND_ARG_INFO(0, a)
    ZEND_ARG_INFO(0, b)
ZEND_END_ARG_INFO()

PHP_FUNCTION(params_add) {
    long a,b;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &a, &b) == FALSE) {
        return;
    }
    RETURN_LONG(a+b);
}

 二、參數相關宏的定義 (Zend/zend_API.h) 數組

#define ZEND_ARG_INFO(pass_by_ref, name)\
{ #name, sizeof(#name)-1, NULL, 0, 0, pass_by_ref, 0, 0 },
//聲明普通參數,能夠用來表示PHP中的int, float, double, string等基本數組類型

#define ZEND_ARG_PASS_INFO(pass_by_ref)\
{ NULL, 0, NULL, 0, 0, pass_by_ref, 0, 0 },
//pass_by_ref爲1時,強制設置後續的參數爲引用類型 

#define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null)\
{ #name, sizeof(#name)-1, #classname, sizeof(#classname)-1, IS_OBJECT, pass_by_ref, allow_null, 0 },
//聲明對象類型的參數

#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null)\
 { #name, sizeof(#name)-1, NULL, 0, IS_ARRAY, pass_by_ref, allow_null, 0 },
//聲明數組類型的參數

#define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null)\
 { #name, sizeof(#name)-1, NULL, 0, type_hint, pass_by_ref, allow_null, 0 },

#define ZEND_ARG_VARIADIC_INFO(pass_by_ref, name)\
{ #name, sizeof(#name)-1, NULL, 0, 0, pass_by_ref, 0, 1 },

#define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args)  \
    static const zend_arg_info name[] = {                                                                       \
        { NULL, 0, NULL, required_num_args, 0, return_reference, 0, 0 },
#define ZEND_BEGIN_ARG_INFO(name, _unused)  \
    ZEND_BEGIN_ARG_INFO_EX(name, 0, ZEND_RETURN_VALUE, -1)
#define ZEND_END_ARG_INFO()     };

 三、展開ZEND_BEGIN_ARG_INFO語句
函數

static const zend_arg_info params_add_arginfo[] = {
    {NULL, 0, NULL, -1, 0, ZEND_RETURN_VALUE, 0},
    {a, sizeof(a)-1, NULL, 0, 0, 0, 0},
    {b, sizeof(b)-1, NULL, 0, 0, 0, 0},
};

 四、參數在zend中的定義(Zend/zend_compile.h) ui

typedef struct _zend_arg_info {
    const char *name; //參數的名稱
    zend_uint name_len; //參數名稱的長度
    char *class_name; //當參數類型爲類時,指定類的名稱
    zend_uint class_name_len; //類名稱的長度
    zend_uchar type_hint; 
    zend_bool allow_null; //是否容許設置爲null
    zend_bool pass_by_reference; //是否設置爲引用,即便用&操做符
}


#define PHP_FUNCTION            ZEND_FUNCTION //函數的實現
#define ZEND_FUNCTION(name)             ZEND_NAMED_FUNCTION(ZEND_FN(name))
#define ZEND_NAMED_FUNCTION(name)       void name(INTERNAL_FUNCTION_PARAMETERS)
#define INTERNAL_FUNCTION_PARAMETERS \
int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC

 五、zend_parse_parameters的參數 this

Boolean         b zend_bool      //布爾值
Long            l long           //長整數
Double          d double         //雙精度浮點數
String          s char *, int    //符串 (也多是空字節)和其長度
Resource        r zval*          // 資源, 保存在 zval*
Array           a zval*          //數組, 保存在 zval*
Object          o zval*          //任何類的)對象, 保存在 zval*
zval            z zval*          //實際的 zval*
zval            O zval*         //由class entry 指定的類的)對象, 保存在 zval*
HashTable       h HashTable*     //數組的哈希表
Function       f char *, int     //函數,方法名 (版本 > php5.1)


|  -代表剩下的參數都是可選參數。若是用戶沒有傳進來這些參數值,那麼這些值就會被初始化成默認值。
/  -代表參數解析函數將會對剩下的參數以 SEPARATE_ZVAL_IF_NOT_REF() 的方式來提供這個參數的一份拷貝,除非這些參數是一個引用。
!  -代表剩下的參數容許被設定爲 NULL(僅用在 a、o、O、r和z身上)。若是用戶傳進來了一個 NULL 值,則存儲該參數的變量將會設置爲 NULL。 

看看官方文檔中提供的幾個例子:code

/* 取得一個長整數,一個字符串和它的長度,再取得一個 zval 值。 */  
long l;  
char *s;  
int s_len;  
zval *param;  

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lsz", &l, &s, &s_len, &param) == 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;  
}  

 在接收參數時還有一個可用的函數zend_parse_parameters_ex,容許咱們傳入一些flags來控制解析參數的動做,使用方式以下所示:對象

int zend_parse_parameters_ex(int flags, int num_args TSRMLS_DC, char *type_spec, ...); 

 目前flags僅能傳入ZEND_PARSE_PARAMS_QUIET這個值,表示函數不輸出任何錯誤信息,以下面的示例:blog

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;  
}  

 可變參數three

因爲PHP支持可變參數,因此在接收可變參數時,使用前面介紹的兩個方法就不太合適,咱們能夠用zend_get_parameters_array_ex()來代替,以下面的示例:ip

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;  
}  

 六、獲取參數數量

PHP沒法根據函數的顯式聲明來對調用進行語法檢查,並且它還支持可變參數,因此咱們就不得不在所調用函數的內部來獲取參數個數。咱們能夠使用宏ZEND_NUM_ARGS來獲取參數個數,以下面的代碼:

if(ZEND_NUM_ARGS() != 2)
{
	WRONG_PARAM_COUNT
}

 這段代碼使用宏WRONG_PARAM_COUNT拋出一個參數個數錯誤

七、更多的參數類型

#define Z_TYPE_P //獲取參數類型 
//IS_NULL|IS_BOOL|IS_LONG|IS_DOUBLE|IS_STRING|IS_ARRAY|IS_RESOURCE|IS_OBJECT
#define Z_BVAL_P //獲取bool類型參數的值
#define Z_LVAL_P //獲取long類型參數的值
#define Z_DVAL_P //獲取float類型參數的值
#define Z_STRVAL_P //獲取string類型參數的值
#define Z_STRLEN_P //獲取string類型參數的長度
#define Z_ARRVAL_P //獲取array類型參數的值
#define Z_RESVAL_P //獲取resource類型參數的值
#define Z_OBJCE_P //獲取object類型參數的值
相關文章
相關標籤/搜索