上篇文章講述了變量的存儲結構zval,今天我們就來學習一下幾個常見變量類型的基本結構。
一、類型一覽
zval中的u1.v.type用來存儲變量的類型,而zval.value存儲的是不同類型對應的值,所以type決定value取值的地方,以下是PHP7所定義的所有類型。
#define IS_UNDEF 0 /* 標記未使用類型 */
#define IS_NULL 1 /* NULL */
#define IS_FALSE 2 /* 布爾類型false */
#define IS_TRUE 3 /* 布爾類型true */
#define IS_LONG 4 /* 長整型 */
#define IS_DOUBLE 5 /* 浮點型 */
#define IS_STRING 6 /* 字符串 */
#define IS_ARRAY 7 /* 數組 */
#define IS_OBJECT 8 /* 對象 */
#define IS_RESOURCE 9 /* 資源 */
#define IS_REFERENCE 10 /* 引用 */
/* 常量相關類型 */
#define IS_CONSTANT 11 /* 常量 */
#define IS_CONSTANT_AST 12 /* 常量抽象語法樹 */
/* 僞類型 */
#define _IS_BOOL 13
#define IS_CALLABLE 14
/* 內部類型 */
#define IS_INDIRECT 15 /* 間接類型 */
#define IS_PTR 17 /* 指針類型 */
- IS_UNDEF:標記未定義,表示數據可以被覆蓋或刪除。
- IS_TRUE/IS_FALSE:本來在PHP5中統一用IS_BOOL來代替,這裏分成兩個可以避免一次類型的檢查。
- IS_REFERRENCE:引用類型,用於處理PHP腳本中的符號
&
。 - IS_PTR:用來解析value.ptr,通常用在函數類型上,比如聲明一個函數或方法。
- IS_INDIRECT:用於解決在全局符號表訪問CV變量的問題。
二、不同類型的結構
剛纔聊到zval.u1.v.type決定了zval.value,下面來看一下zend_value結構體的定義。
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; /* zval類型 */
void *ptr; /* 指針類型 */
zend_class_entry *ce; /* class類型 */
zend_function *func; /* function類型 */
struct {
uint32_t w1;
uint32_t w2;
} ww;
} zend_value;
基本可以看出該結構體的變量和上文定義的類型是一一對應的,我們抽取幾個常用的類型講述一下。
2.1、字符串
字符串str
對應的結構體是zend_string,它有四個成員,定義如下。
struct _zend_string {
zend_refcounted_h gc;
zend_ulong h; /* hash value */
size_t len;
char val[1];
};
- gc:變量的引用計數信息,用於內存管理。
- h:字符串通過Time33算法計算的到的Hash值,避免了在數組操作中hash值的重複計算,據說提高了PHP7百分之5的性能。
- len:字符串的長度。
- val:字符串的內容,val[1]並不表示只能存儲1個字節,在字符串分配時實際上是操作了
malloc(sizeof(zend_string)+字符串你長度)
,也就是會多分配一些內存,而多出來的內存起始位置就是val,這樣就可以將字符串直接存儲到val,並通過val進行讀取,這種採用了柔性數組的方式,讀寫效率更高。
2.2、數組
成員變量arr對應的結構體是zend_array,它就是你可能有所耳聞的HashTable,zend_array結構體定義如下。
struct _zend_array {
zend_refcounted_h gc;
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar flags,
zend_uchar nApplyCount,
zend_uchar nIteratorsCount,
zend_uchar reserve)
} 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;
};
- nTableMask:根據key的hash code映射元素存儲位置時有用到,它的值是nTableSize的負數,nTableMask=-nTableSize。
- arData:數組的每一個元素都保存在這裏,默認指向第一個元素。
- nNumUsed:當前使用的Bucket數,但不都是有效的,因爲有的Bucket雖然被unset了但是沒有馬上被刪除,而是做了IS_UNDEF標記。
- nNumOfElements:有效的Bucket數,這個就與上面不同了,這裏記錄的是真實有效的Bucket數量。
- nTableSize:數組的總容量。
- nIternalPointer:當前遍歷的指針。
- nNextFreeElement:下一個索引的值,比如每次給數組新增數據時,該值就會加一,
$a[] = 1
。 - pDestructor:析構函數,在刪除或覆蓋某個元素時,調用該函數,可以對舊元素進行清理。
- u:這裏的u主要還是起到輔助作用,比如flags用來設置散列表的一些屬性是否持久化、是否已經初始化等。
2.3、對象
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:引用計數。
- handle:一次請求期間對象的編號,每一個對象都有一個唯一的編號,與創建的先後順序有關,主要是在垃圾回收的時候使用。
- ce:該對象所屬的類。
- handlers:對象操作的處理函數,比如成員屬性的讀寫、成員方法的獲取、對象的銷燬克隆等。
- properties:普通成員屬性的哈希表,初始化對象時該值爲NULL。
- properties_table:用來存儲普通成員的屬性值,對象對非靜態成員屬性的操作就是通過這個數組。
參考文獻
- 《PHP7內核剖析》
- 《PHP7底層設計與源碼實現》