咱們都知道PHP的變量是弱類型的,聲明的時候無需指定類型。那麼這裏面具體是怎麼實現的呢?這就得從變量的基礎結構提及了。php
在源碼文件 zend_type.h 中,能夠看到 zval 的定義:node
typedef struct _zval_struct zval;
struct _zval_struct {
zend_value value; /* value */
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar type, /* active type */
zend_uchar type_flags,
zend_uchar const_flags,
zend_uchar reserved) /* call info for EX(This) */
} v;
uint32_t type_info;
} u1;
union {
uint32_t next; /* hash collision chain */
uint32_t cache_slot; /* literal cache slot */
uint32_t lineno; /* line number (for ast nodes) */
uint32_t num_args; /* arguments number for EX(This) */
uint32_t fe_pos; /* foreach position */
uint32_t fe_iter_idx; /* foreach iterator index */
uint32_t access_flags; /* class constant access flags */
uint32_t property_guard; /* single property guard */
uint32_t extra; /* not further specified */
} u2;
}
複製代碼
zval 的結構由一個保存變量類型的值或指針的 union 聯合體 zend_value 以及兩個 union 聯合體 u1 和 u2 組成數組
u1的做用是用來保存變量類型及其信息,其裏面的字段用處以下:緩存
type:記錄變量類型。 便可經過 u2.v.type 來訪問到安全
type_flags:對應變量特有類型的標記(如常量類型,需引用計數類型,不可變類型),不一樣類型的變量對應的 flag 不同。php7
const_flags:常量類型的標記數據結構
reserved:保留字段函數
u2 主要是輔助做用,因爲結構體的內存對齊,因此 u2 的的這塊空間有或者沒有 u2 都是已經佔據空間了,因此就利用起來。u2的輔助字段裏面記錄了不少類型信息,這些信息對內部功能有很大的好處,或提高緩存友好性或減小了內存尋址的操做。這裏介紹其中部分字段。性能
next:用來解決哈希衝突問題(哈希衝突這個目前還不懂),記錄衝突的下一個元素位置。優化
cache_slot:運行時緩存。在執行函數時會優先去緩存中查找,若緩存中沒有,再去全局的 function 表中查找。
num_args:函數調用時傳入參數的個數
access_flags:對象類的訪問標識,如public protected private 這些。
typedef union _zend_value {
zend_long lval; /* 整型*/
double dval; /* 浮點型 */
zend_refcounted *counted;
zend_string *str;
zend_array *arr;
zend_object *obj;
zend_resource *res;
zend_reference *ref;
zend_ast_ref *ast;
zval *zv;
void *ptr;
zend_class_entry *ce;
zend_function *func;
struct {
uint32_t w1;
uint32_t w2;
} ww;
} zend_value;
複製代碼
從 zend__value 中能夠看出,long、double 類型直接存儲值,而其它類型都爲指針,指向各自的結構。因此,因爲 zval 這樣的結構,PHP 變量在聲明的時候不用顯示的指定其類型,由於無論你賦給變量什麼類型的值,它都能幫你找到對應的存儲結構。
以值爲字符串的變量爲例,其結構是這樣的:
能夠看到 php7 的 zval 總的只佔 16 個字節,相比 PHP5 的 zval 所佔用的 48 個字節節省了很大的內存。
此外,在 PHP5 中,全部的變量都在堆中申請,可是對於臨時變量來講,沒有必要在堆中申請。因此在 PHP7 中對此作了優化,臨時變量是直接在棧中申請的。
下面介紹幾個常見類型的變量結構,其餘更多的類型,可自行查看源碼。
對於整型和浮點型,因爲其佔用空間小,在 zval 中是直接存儲的 整型的值是存在 lval 裏,浮點型值則是存儲在 dval 裏。
typedef union _zend_value {
zend_long lval; /* 整型*/
double dval; /* 浮點型 */
...
}
複製代碼
PHP 7 中定義了新的字符串結構體。結構以下:
struct _zend_string {
zend_refcounted_h ;
zend_ulong h; /* hash value */
size_t len;
char val[1];
};
複製代碼
上面各個字段的意思:
gc: 變量引用信息,全部用到引用計數的變量類型都會有這個結構。
h: 哈希值,數組中計算索引時會用到。(聽說這個操做爲 PHP7 提升了 5% 的性能)
len: 字符串長度,經過這個值保證二進制安全
val: 字符串內容,變長struct,分配時按len長度申請內存 數組
array 是 PHP 中很是強大的一個數據結構,它的底層實現就是普通的有序HashTable,這裏簡單看下它的結構。後續再具體深刻。
typedef struct _zend_array HashTable;
struct _zend_array {
zend_refcounted_h gc;
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar flags,
zend_uchar nApplyCount,
zend_uchar nIteratorsCount,
zend_uchar consistency)
} v;
uint32_t flags;
} u;
uint32_t nTableMask;
Bucket *arData;
uint32_t nNumUsed;
uint32_t nNumOfElements;
uint32_t nTableSize;
uint32_t nInternalPointer;
zend_long nNextFreeElement;
dtor_func_t pDestructor;
}
複製代碼
###對象
PHP7 的對象結構也是從新設計了,和 PHP5 的實現有了很大的不一樣。
struct _zend_object {
zend_refcounted_h gc;
uint32_t handle;
zend_class_entry *ce;
const zend_object_handlers *handlers;
HashTable *properties;
zval properties_table[1];
};
複製代碼
這裏介紹下其中幾個字段:
gc:gc頭部
*ce:對象對應的 class 類
*properties :HashTable結構,key 爲對象的屬性名,value 是屬性值在properties_tables數組中的偏移量,經過偏移量在 properties_talbe 找到對應的屬性值。
properties_talbe[1]:存儲對象的屬性值
ok,先寫這到這裏。
參考資料
《PHP7 底層設計與源碼實現》