1. 程式人生 > >跟廠長學PHP7核心(七):常見變數型別的基本結構

跟廠長學PHP7核心(七):常見變數型別的基本結構

上篇文章講述了變數的儲存結構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:用來儲存普通成員的屬性值,物件對非靜態成員屬性的操作就是通過這個陣列。