之前對於php的內部生命周期和Zend引擎的線程安全機制做了一個介紹,這裡這篇文章則是主要介紹php的內部變量是如何實現的。
了解了這些實現的方法之後,對於寫php,尤其是進行php擴展開發感覺相當有幫助。
php是一種類型比較松散的語言,與C相比不需要在使用變量前給出類型,直接用就可以。為了實現這一點,php必須在數據類型的定義上做一些工作。
數據類型:
最基本的類型被稱為是zval或者說Zend Value,定義在Zend/zend.h頭文件中。 typedef struct _zval_struct {void describe_zval(zval *foo)
{
if (foo->type == IS_NULL) {
php_printf("The variable is NULL");
} else {
php_printf("The variable is of type %d", foo->type);
}
}
和
void describe_zval(zval *foo)
{
if (Z_TYPE_P(foo) == IS_NULL) {
php_printf("The variable is NULL");
} else {
php_printf("The variable is of type %d",
Z_TYPE_P(foo));
}
}
數據值
通過一些宏可以獲取不同類型的zval的值: BVAL(): BooleanLVAL(): longDVAL(): double 這個函數針對三種不同的zval類型,分別利用Z_TYPE進行了類型判斷。然後利用相應的值提取的宏進行取值。void display_values(zval boolzv, zval *longpzv,
zval **doubleppzv)
{
if (Z_TYPE(boolzv) == IS_BOOL) {
php_printf("The value of the boolean is: %s\n",
Z_BVAL(boolzv) ? "true" : "false");
}
if (Z_TYPE_P(longpzv) == IS_LONG) {
php_printf("The value of the long is: %ld\n",
Z_LVAL_P(longpzv));
}
if (Z_TYPE_PP(doubleppzv) == IS_DOUBLE) {
php_printf("The value of the double is: %f\n",
Z_DVAL_PP(doubleppzv));
}
}
void display_string(zval *zstr)
{
if (Z_TYPE_P(zstr) != IS_STRING) {
php_printf("The wrong datatype was passed!\n");
return;
}
PHPWRITE(Z_STRVAL_P(zstr), Z_STRLEN_P(zstr));
}
數據創建:
想要創造一個變量並分配空間的malloc(sizeof(zval))在php這裡並不可行。應該使用MAKE_STD_ZVAL(pzv), 它對空間的分配進行了優化,並且會自動的初始化refCount(表示這個變量被引用的次數)和is_ref(是否是強制引用)這兩個性質。注意它的輸入是一個指針.
數據的存儲
數據的存儲都在符號表中。 symbol table,每當創建一個新的變量的時候,Zend都保存這個值到這個內部的數組中去。 符號表在RINIT之前創建,在RSHUTDOWN之後銷毀。struct _zend_executor_globals {
...
HashTable symbol_table;
HashTable *active_symbol_table;
...
};{
zval *fooval;
MAKE_STD_ZVAL(fooval); //首先分配空間,設置變量
ZVAL_STRING(fooval, "bar", 1); //然後賦值,創建一個copy,你不能直接操作常字符串
ZEND_SET_SYMBOL(EG(active_symbol_table), "foo", fooval); // 在符號表中注冊,foo是一個label
}所謂active_symbol_table指的是程序執行當前的符號表,在進入一個函數之後,會有它自己對應的符號表,就類似於C中針對一個函數自己的棧空間。而當退出了函數之後,它的符號表會被銷毀,這時候又回到了下面這個狀態:
EG(active_symbol_table) == &EG(symbol_table), 這個時候並沒有進入函數。
{
zval **fooval;
if (zend_hash_find(EG(active_symbol_table),
"foo", sizeof("foo"),
(void**)&fooval) == SUCCESS) {
php_printf("Got the value of $foo!");
} else {
php_printf("$foo is not defined.");
}
}這個函數首先查找符號表,找到名字為“foo”的變量,然後返回到fooval中。下面著重解釋兩個問題:
為什麼要聲明一個zval ** fooval 然後還要通過&fooval並且轉換為(void **)的形式?為什麼要用sizeof("foo")
對第一個問題,要考慮到我們尋找的目標是一個zval*,所以要把它看作一個整體。利用這種寫法可以避免編譯告警。
第二個問題,使用sizeof(label)主要是為了表示字符串常量label的尾部,這裡使用4也是可以的,但是通用性不夠。
以上就是php內部變量的一些介紹,為了能夠區分不同的類型、設置獲取變量值以及在符號表中增加和查找變量,這些知識必不可少。