之前對於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內部變量的一些介紹,為了能夠區分不同的類型、設置獲取變量值以及在符號表中增加和查找變量,這些知識必不可少。