閱讀本節前如對PHP中變數的結構還不瞭解請前往:PHP核心中的變數
變數的作用是存放資料,由於PHP中變數不但儲存著值還儲存著類型,所以不但要為變數賦值,同時還要為變數設定類型。
1) 長整型(整型)類型變數:PHP核心中整數全部是長整型的(long),其值就儲存在之前講過的PHP變數zval結構的聯合體value中的lval欄位中,相應的類型為IS_LONG,賦值的代碼如下:
zval *new_var;
MAKE_STD_ZVAL(new_var);
new_var->value.lval = 12;
new_var->type = IS_LONG;
但為了相容性最好使用ZVAL_LONG宏賦值:
zval *new_var;
MAKE_STD_ZVAL(new_var);
ZVAL_LONG(new_var,12);
上面的例子跟代碼效果相同;
2) 雙精確度(浮點數)類型變數:同長整型賦值差別只在變數類型為IS_DOUBLE,ZVAL_DOUBLE(new_var,12.56);
3) 字串類型變數:除了儲存字串的值外,還需要儲存字串的長度(提供給strlen()等函數使用)。字串的值儲存在zval結構的value聯合體的str結構體中的val欄位中,而字串長度儲存在str結構體中的len欄位,並且相應的類型設定為IS_STRING;另外值得注意的是用來儲存字串值的內在塊應該使用ZEND引擎記憶體管理函數去申請這樣可以避免自己管理這些記憶體還可以讓ZEND引擎處理起來更方便,賦值代碼如下:
zval *new_var;
char *str = "this is n new string variable";
MAKE_STD_ZVAL(new_var);
new_var->value.str.len = strlen(str);
new_var->value.str.val = estrdup(str); //estrdup是ZEND引擎的記憶體管理函數
new_var->type = IS_STRING;
使用ZEND_STRING宏來賦值:ZVAL_STRING(new_var,str,1);這裡第三個參數指明該字串是否需要被複製(使用ZEND引擎記憶體管理函數),當設定為1時,將會複製這二個參數指向的字串,設定為0時,直接把val欄位指向這二個參數指向的字串(可理解為引用賦值)。
如果只想取字串的一部分或者已經知道字串的長度進行賦值,可使用宏ZEND_STRINGL(zval,string,length,duplicate)完成這項工作,參數length就是指定字串的長度,ZVAL_STRINGL宏要比ZVAL_STRING宏快,其定義如下:
#define ZVAL_STRINGL(z,s,l,duplicate) { \
char *_s = (s); int _l = l; \
(z)->value.str.len = _l; \
(z)->value.str.val = (duplicate ? estrndup(_s,_l) : _s); \
(z)->type =IS_STRING; \
}
從定義可看出字串的長度(len欄位)被直接設定成length參數的值所以不再用strlen()去計算字串的長度,如果想建立一個Null 字元串可將其長度設定為0,並且把empty_string作為字串的值即可
new_var->value.str.len = 0;
new_var->value.str.val = empty_string;
new_var->type = IS_STRING;或者使用ZVAL_EMPTY_STRING宏完成:
ZVAL_EMPTY_STRING(new_string); //這裡沒有漏掉參數確定不是ZVAL_EMPTY_STRING(new_var,new_string);?
4) 布爾類型變數:布爾類型的賦值和整型的賦值差不多,區別在於把資料類型欄位設定為IS_BOOL,並且lval欄位的值只能是0或1,ZVAL_BOOL宏代碼如下:ZVAL_BOOL(new_var,1);
5) 數群組類型變數:數組在PHP中扮演了重要的角色,PHP的強大可以說是因為數組的靈活,在核心中數組是使用雜湊表儲存的(HashTable);在為變數賦值數組時,先要建立一個HashTable,然後將其儲存在zval.val容器的ht欄位中,Zend引擎提供了一個簡單的介面array_init()來完成這項工作,代碼如下:
zval *new_var;
MAKE_STD_ZVAL(new_var);
array_init(new_array);
上面的代碼相當於
建立一個空的數組之後就可往裡面添加元素了,有 平行線鐵API可供使用,下面列出可使用的所有API(成功時都返回SUCCESS,失敗時都返回FAILURE):
關聯陣列的API:
add_assoc_long(zval *array,char *key,long n); //相當於$array["key"]=10;
add_assoc_unset(zval *array,char *key); //$array["key"] = NULL;
add_assoc_bool(zval *array,char *key,int b);
add_assoc_resource(zval *array,char *key,int r);
add_assoc_double(zval *array,char *key,double d);
add_assoc_string(zval *array,char *key,char *str,int duplicate); //$array["key"] = "string";
add_assoc_stringl(zval *array,char *key,char *str,uinit length,int duplicate); //添加指定長度的字串
add_assoc_zval(zval *array,char *key,zval *value); //添加一個zval結構,這在添加另外一個數組、對象或流等資料時很有用,相當於$array["key"] = $value;
索引數組的API:索引數組和關聯陣列很相似,差別在於下標是字串還是整型,所以把上面的原型中所有的char *key換成unit idx就是索引數組的API了,就不再列出了。
上面提到的API只是抽象HashTable的API函數而已,也可以直接使用HashTable的API函數進行操作,如添加一個元素到數組sk 使用zend_hash_update()函數進行操作。使用API添加一個元素到數組的例子:
zval array,element;
char *key = "key for search";
char *value = "value_for_element";
MAKE_STD_ZVAL(array);
MAKE_STD_ZVAL(element);
array_init(array);
ZVAL_STRING(element,value,1);
add_assoc_zval(array,key,element);
6) 物件類型變數:在講解變數賦予物件類型的值之前,先要瞭解PHP在對象跟數組的關係:對象可轉化成數組(此過程無法復原,因為對象的functions會丟失),對象的屬性和數組是可相互轉換的。對象的函數等應該是儲存在一個HashTable中?這個問題留到瞭解PHP核心中的函數的時候再來解決。
建立一個對象的API代碼如下:
zval *new_object;
MAKE_STD_ZVAL(new_object);
if(object_init(new_object)!=SUCCESS) {
RETURN_NULL();
}
Zend引擎為對象添加屬性的API如下:
add_property_long(zval *object,char
key,long l);
object,char *key);
add_property_bool(zval *object,char *key,int b);
add_property_resource(zval *object,char *key,long r);
add_property_double(zval *object,char *key,double d);
add_property_string(zval *object,char *key,char *str,int duplicate);
add_property_stringl(zval *object,char *key,char *str,uint length,int duplicate);
add_property_zval(zval *object,char *key,zval *container);
如添加一個名字為“name”類型為字串的屬性如下:
zval *new_object;
MAKE_STD_ZVAL(new_object);
if(object_init(new_object)!=SUCCESS) {
RETURN_NULL();
}
add_property_string(new_object,"name","James",1);
7) 資源類型變數:建立資源類型的變數比建立其他類型的變數煩瑣一點,嚴格來說,資源不是資料類型,它是一個可以維護任何資料類型的抽象(就像C語言中的指標)。所有的資源都是儲存在一個Zend內部的資源清單中,列表中的每人資源都有一個指向實際資料的指標,如果對PHP核心足夠瞭解的話,可以直接存取這些資源,不過為了相容性和安全性還是建議使用Zend引擎提供的API訪問它們。
當資源失去所有的引用的時候就會觸發相應的解構函式,而這個解構函式是有資源自己的提供的,原因是zend引擎不能管理資源的實際資料,所以為了防止記憶體流失就必須提供解構函式釋放這些記憶體。
資料庫連接和檔案操作符是PHP資源類型的,另外也可以儲存自訂的資料結構等其他的類型的資料為資源類型。
使用zend_register_list_destructors_ex()函數可以用來註冊一個資源變數的解構函式,該函數返回一個資源的控制代碼,資源控制代碼的作用就是把資源與資源的解構函式相關聯,以下是zend_register_list_destructors_ex()函數的定義:
ZEND_API int_zend_register_list_destructors_ex(rsrc_dtor_func_t ld,rsrc_dtor_func_t pld,char *type_name,int module_number);其參數說明如下:
ld:普通資源的解構函式,
pld:持久化資源的解構函式,
type_name:為資源指定一個名稱,在PHP內部為某個資源類型起個名字是好習慣,使用者調用var_dump($resource)時就可取得該資源的名稱。
module_number:在模組的PHP_MINIT_FUNCTION函數中會自動定義,因此可將其忽略。
ld和pld必須要至少提供一個,另外一個解構函式可以簡單的設為NULL.
資源的解構函式原型必須如下定義:void resource_destruction_handler(zend_rsrc_entry *rsrc TSRMLS_DC);
參數rsrc是一個指向zend_rsrc_list_entry結構體的指標:
typedef struct _zend_rsrc_list_entry {
void *ptr; //用於儲存資源的真正資料的地址,一般在解構函式中釋放ptr欄位指向的記憶體
int type;
int refcount;
} zend_rsrc_list_entry;
例如定義一個鏈表資料結構如下:
typedef struct _ListNode {
struct _ListNode *next;
void *data;
} ListNode;
然後資源變數儲存的就是這個鏈表的頭指標,解構函式可以這樣編寫:
void list_destroy_handler(zend_rsrc_list_entry
rsrc TSRMLS_DC) {
ListNode current,next;
)rsrc->ptr; //rsrc->ptr就是一個指標類型,這裡是指標類型轉換會把ptr所指向的資源轉換成ListNode的鏈表資源?
while(current) {
next = current->next;
free(current);
current = next;
}
}
這樣就可以釋放整個鏈表的記憶體。
while