轉載自:http://terrylee.me/blog/post/2011/02/12/php-extension-part1.aspx
在前面的文章多次提到了zval結構,其實所有使用者定義的變數在PHP中都是用zval類型來表示的,當我門
使用zend_parse_parameters函數解析參數時,Zend引擎會根據相應的資料類型進行類型轉換,而由於PHP中的數組、對象和資源類
型,在C語言中沒有對應的類型,所以無法進行類型轉換,它們都使用zval表示,先看一下zval結構定義:
typedef pval zval;typedef struct _zval_struct zval;struct _zval_struct { /* Variable information */ zvalue_value value; /* value */ unsigned char type; /* active type */ unsigned char is_ref; short refcount;};
zval結構的定義使用了C語言中的等位型別,各個欄位說明如下:
value 變數內容的聯合,參見“表3.6 Zend zvalue_value 結構”
type 變數的類型。“表3.7 Zend 變數類型”給出了一個完整的變數類型列表
is_ref 0 表示這個變數還不是一個引用。1 表示這個變數還有被別的變數所引用
refcount 表示這個變數是否仍然有效。每增加一個對這個變數的引用,這個數值就增加 1。反之,每失去一個對這個變數的引用,該值就會減1。當引用計數減為0的時候,就說明已經不存在對這個變數的引用了,於是這個變數就會自動釋放
變數類型定義:
IS_NULL 表示是一個空值 NULL
IS_LONG 是一個(長)整數
IS_DOUBLE 是一個雙精確度的浮點數
IS_STRING 是一個字串
IS_ARRAY 是一個數組
IS_OBJECT 是一個對象
IS_BOOL 是一個布爾值
IS_RESOURCE 是一個資源(關於資源的討論,我們以後會在適當的時候討論到它)
IS_STRING 是一個常量
zvalue_value結構定義:
typedef union _zvalue_value { long lval; /* long value */ double dval; /* double value */ struct { char *val; int len; } str; HashTable *ht; /* hash table value */ struct { zend_class_entry *ce; HashTable *properties; } obj;} zvalue_value;
zvalue_value結構的說明如下:
lval 如果變數類型為 IS_LONG、IS_BOOLEAN 或 IS_RESOURCE 就用這個屬性值
dval 如果變數類型為 IS_DOUBLE 就用這個屬性值
str 如果變數類型為 IS_STRING 就訪問這個屬性值。它的欄位 len 表示這個字串的長度,欄位 val 則指向該字串。由於 Zend 使用的是 C 風格的字串,因此字串的長度就必須把字串末尾的結束符 0×00 也計算在內
ht 如果變數類型為數組,那這個 ht 就指向數組的雜湊表入口
obj 如果變數類型為 IS_OBJECT 就用這個屬性值
給定一個具體的zval,可用三個便利的宏中的一個測試它的類型:Z_TYPE(zval)、Z_TYPE_P(zval*)或Z_TYPE_PP(zval**)。三者之間僅有的功能上的區別在於傳入的變數所期望的間接的層級。如下面的樣本:
PHP_FUNCTION(hello_dump){ zval *uservar; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &uservar) == FAILURE) { RETURN_NULL(); } switch (Z_TYPE_P(uservar)) { case IS_NULL: php_printf("NULL/n"); break; case IS_BOOL: php_printf("Boolean: %s/n", Z_LVAL_P(uservar) ? "TRUE" : "FALSE"); break; case IS_LONG: php_printf("Long: %ld/n", Z_LVAL_P(uservar)); break; case IS_DOUBLE: php_printf("Double: %f/n", Z_DVAL_P(uservar)); break; case IS_STRING: php_printf("String: "); PHPWRITE(Z_STRVAL_P(uservar), Z_STRLEN_P(uservar)); php_printf("/n"); break; case IS_RESOURCE: php_printf("Resource/n"); break; case IS_ARRAY: php_printf("Array/n"); break; case IS_OBJECT: php_printf("Object/n"); break; default: php_printf("Unknown/n"); } RETURN_TRUE;}
編寫一個簡單的測試指令碼:
<?php hello_dump(1234); echo '<br/>'; hello_dump('terrylee'); echo '<br/>'; hello_dump(array('foo', 'bar'));?>
運行後效果如下:
在PHP擴充中對於使用者傳過來的參數,本質上都是一個zval結構,我們需要調用一些轉換函式進行強制類型轉換(zend_parse_parameters函數會對基本類型做轉換),Zend引擎提供了convert_to_xxx系列函數協助我們進行類型轉換:
convert_to_boolean_ex()
強制轉換為布爾類型。若原來是布爾值則保留,不做改動。長整型值0、雙精確度型值0.0、Null 字元串或字串‘0’還有空值 NULL 都將被轉換為
FALSE(本質上是一個整數 0)。數組和對象若為空白則轉換為 FALSE,否則轉為 TRUE。除此之外的所有值均轉換為
TRUE(本質上是一個整數 1)。
convert_to_long_ex()
強制轉換為長整型,這也是預設的整數類型。如果原來是空值NULL、布爾型、資源當然還有長整型,則其值保持不變(因為本質上都是整數
0)。雙精確度型則被簡單取整。包含有一個整數的字串將會被轉換為對應的整數,否則轉換為 0。空的數組和對象將被轉換為 0,否則將被轉換為 1。
convert_to_double_ex()
強制轉換為一個雙精確度型,這是預設的浮點數類型。如果原來是空值 NULL
、布爾值、資源和雙精確度型則其值保持不變(只變一下變數類型)。包含有一個數位字串將被轉換成相應的數字,否則被轉換為
0.0。空的數組和對象將被轉換為 0.0,否則將被轉換為 1.0。
convert_to_string_ex()
強制轉換為數組。若原來就是一數組則不作改動。對象將被轉換為一個以其屬性為鍵名,以其屬性值為索引值的數組。(方法強制轉換為字串。空值 NULL
將被轉換為空白字串。布爾值 TRUE 將被轉換為 ‘1’,FALSE
則被轉為一個Null 字元串。長整型和雙精確度型會被分別轉換為對應的字串,數組將會被轉換為字串‘Array’,而對象則被轉換為字串‘Object’。
convert_to_array_ex(value)
強制轉換為數組。若原來就是一數組則不作改動。對象將被轉換為一個以其屬性為鍵名,以其屬性值為索引值的數組。(方法將會被轉化為一個‘scalar’鍵,
索引值為方法名)空值 NULL 將被轉換為一個空數組。除此之外的所有值都將被轉換為僅有一個元素(下標為 0)的數組,並且該元素即為該值。
convert_to_object_ex(value)
強制轉換為對象。若原來就是對象則不作改動。空值 NULL 將被轉換為一個Null 物件。數組將被轉換為一個以其鍵名為屬性,索引值為其屬性值的對象。其他類型則被轉換為一個具有‘scalar’屬性的對象,‘scalar’屬性的值即為該值本身。
convert_to_null_ex(value)
強制轉換為空白值 NULL。
關於zval結構的介紹就到這裡了。