1. 程式人生 > >PHP變數在核心中的儲存方式

PHP變數在核心中的儲存方式

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;

    ...
}