PHP變數在核心中的儲存方式
阿新 • • 發佈:2018-12-31
PHP是弱型別語言,也就是說一個PHP變數可以儲存任何的資料型別。但是PHP是使用C語言編寫的,而C語言是強型別的語言,每個變數都有固定型別,不能隨意改變變數的型別(可以通過強型別轉換改變,不過有可能出現問題),在Zend引擎中是怎麼做到一個變數儲存任何的資料型別呢?
開啟Zend/zend.h檔案,會發現以下一些結構體:
typedef union _zvalue_value {
long lval; /* long value */
double dval; /* double value */
struct {
char *val;
int len;
} str;
HashTable *ht; /* hash table value */
zend_object_value obj;
zend_ast *ast;
} zvalue_value;
struct _zval_struct {
/* Variable information */
zvalue_value value; /* value */
zend_uint refcount__gc;
zend_uchar type; /* active type */
zend_uchar is_ref__gc;
};
zval結構體就是通常用到的PHP變數(如$variable)在核心中的表示方式。在zval結構體中,可以看到4個成員變數,分別是
zvalue_value value
: 變數的值,PHP變數的值就儲存在這裡。zend_uint refcount__gc
: 變數引用數,變數引用計算器。zend_uchar type;zend_uchar type
: 變數的型別。zend_uchar is_ref__gc
: 變數是否被引用。
zval結構體的value成員變數是一個zvalue_value聯合體,PHP能夠保持任何的結構型別就是因為這個聯合體。從zvalue_value聯合體的成員變數中可以看到,不同的型別會儲存到不同的成員變數中,這樣就實現了PHP變數可以儲存任何資料型別。例如,當變數是整數型別時,會儲存到value的lval成員變數中;當變數的型別是字串時,又會儲存到value的str成員變數中。如下表展示了不同型別儲存到對應的成員變數中。
PHP語言層型別 | 儲存在zvalue_value的成員變數 |
---|---|
long,bool,resoure | lval |
double | dval |
string | str(len儲存字串的長度,val儲存字串的值) |
array | ht |
object | obj |
現在已經解決了一個PHP變數可以儲存任意型別的問題,但是另一個問題又出現了,就是Zend引擎是怎麼知道這個變數儲存的是什麼型別呢?我們注意到,zval結構體中有個type成員變數,這個成員變數就是儲存一個PHP變數的型別。
Zend引擎定義了幾種變數型別,如下:
#define IS_NULL 0
#define IS_LONG 1
#define IS_DOUBLE 2
#define IS_BOOL 3
#define IS_ARRAY 4
#define IS_OBJECT 5
#define IS_STRING 6
#define IS_RESOURCE 7
每一個巨集定義對應PHP語言層的一種型別,例如當zval的type成員變數等於IS_STRING時(zval.type==IS_STRING),說明這個變數的型別時字串型別。
巨集定義 | 表示型別 |
---|---|
IS_NULL | NULL型別(null) |
IS_LONG | 整數型別(int) |
IS_DOUBLE | 浮點型別(float) |
IS_STRING | 字串型別(string) |
IS_ARRAY | 陣列型別(array) |
IS_OBJECT | 物件型別(object) |
IS_BOOL | 布林型別(bool) |
IS_RESOURCE | 資源型別(resource) |
可以通過下面的程式碼列印一個zval的型別:
switch(zval.type) {
case IS_NULL:
php_printf("zval type is null\n");
break;
case IS_STRING:
php_printf("zval type is string\n");
break;
case IS_LONG:
php_printf("zval type is long\n");
break;
case IS_ARRAY:
php_printf("zval type is array\n");
break;
...
}