跟廠長學PHP7核心(七):常見變數型別的基本結構
阿新 • • 發佈:2018-12-10
上篇文章講述了變數的儲存結構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:用來儲存普通成員的屬性值,物件對非靜態成員屬性的操作就是通過這個陣列。