跟廠長學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:用來存儲普通成員的屬性值,對象對非靜態成員屬性的操作就是通過這個數組。

參考文獻

  • 《PHP7內核剖析》
  • 《PHP7底層設計與源碼實現》
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章