轉載自: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、空字元串或字元串‘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 則被轉為一個空字元串。長整型和雙精度型會被分别轉換為對應的字元串,數組将會被轉換為字元串‘Array’,而對象則被轉換為字元串‘Object’。
convert_to_array_ex(value)
強制轉換為數組。若原來就是一數組則不作改動。對象将被轉換為一個以其屬性為鍵名,以其屬性值為鍵值的數組。(方法将會被轉化為一個‘scalar’鍵, 鍵值為方法名)空值 NULL 将被轉換為一個空數組。除此之外的所有值都将被轉換為僅有一個元素(下标為 0)的數組,并且該元素即為該值。
convert_to_object_ex(value)
強制轉換為對象。若原來就是對象則不作改動。空值 NULL 将被轉換為一個空對象。數組将被轉換為一個以其鍵名為屬性,鍵值為其屬性值的對象。其他類型則被轉換為一個具有‘scalar’屬性的對象,‘scalar’屬性的值即為該值本身。
convert_to_null_ex(value)
強制轉換為空值 NULL。
關于zval結構的介紹就到這裡了。