php內核學習-基本變量zval 和zend_array 和zend_string

1.zval是php變量底層定義的結構,一共16字節

typedef struct _zval_struct     zval;
struct _zval_struct {   
           zend_value        value;     
           union u1;    
           union u2;
};

zval大小

zval 的詳細結構如下:
zend_value 是一個聯合體 它的大小是8個字節
u1 聯合體 4個字節
u2 聯合體 4個字節
共16字節

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;    //4個字節
		//ZEND_ENDIAN_LOHI_4 定義  define ZEND_ENDIAN_LOHI_4(a, b, c, d)    d; c; b; a;
		//typedef unsigned char zend_uchar;
		uint32_t type_info;  //4個字節
	} u1;
	union {
		uint32_t     next;                 /* hash collision chain  用來解決哈希衝突問題,記錄衝突的下一個元素位置*/
		uint32_t     cache_slot;           /* literal cache slot  運行時緩存。在執行函數時會優先去緩存中查找,若緩存中沒有,會在全局的function表中查找*/
		uint32_t     lineno;               /* line number (for ast nodes) 文件執行的行號,應用在AST節點上 */
		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;
};
typedef union _zend_value {
	zend_long         lval;				/* long value */
	double            dval;				/* double value */
	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_uchar type /* active type */
type代表含義:

#define IS_UNDEF  0
#define IS_NULL  1
#define IS_FALSE 2
#define IS_TRUE  3
#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


struct array

array的數據結構定義在zend_types.h中

typedef struct _zend_array      zend_array;
typedef struct _zend_array      HashTable;
struct _zend_array {
	zend_refcounted_h gc;    //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;   //散列函數映射元素在arData數組中的下標
	Bucket           *arData;       //存數組元素的頭指針,存儲的元素結構是Bucket類型的
	uint32_t          nNumUsed;     //已經bucket的個數
	uint32_t          nNumOfElements;    //數組實際存儲的元素個數
	uint32_t          nTableSize;     //數組的容量(大小)
	uint32_t          nInternalPointer;
	zend_long         nNextFreeElement;    //下個可用的數值索引,eg:arr[]=1; arr['a']=2; arr[]=3; 則nNextFreeElement=2
	dtor_func_t       pDestructor;
};
typedef struct _Bucket{
	zval	val;		//存儲具體的value
	zend_ulong	h;		//哈希值或者數值索引
	zend_string	*key; 	//存儲元素的key
}

基本實現

<?php
$a['foo'] = 1;
$a[] = 22;
$a['s'] ='df';
$a['x'] = 4;
echo $a;
33087           z = _get_zval_ptr_cv_undef(opline->op1.var EXECUTE_DATA_CC);
(gdb) n
33089           if (Z_TYPE_P(z) == IS_STRING) {
(gdb) p z
$25 = (zval *) 0x7ffff5a1e080     //數組的底層也是zval
(gdb) p *z
$26 = {value = {lval = 140737314694368, 
    dval = 6.9533472278435683e-310, counted = 0x7ffff5a624e0, 
    str = 0x7ffff5a624e0, arr = 0x7ffff5a624e0, 
    obj = 0x7ffff5a624e0, res = 0x7ffff5a624e0, 
    ref = 0x7ffff5a624e0, ast = 0x7ffff5a624e0, 
    zv = 0x7ffff5a624e0, ptr = 0x7ffff5a624e0, 
    ce = 0x7ffff5a624e0, func = 0x7ffff5a624e0, ww = {
      w1 = 4121306336, w2 = 32767}}, u1 = {v = {type = 7 '\a', 
      type_flags = 20 '\024', const_flags = 0 '\000', 
      reserved = 0 '\000'}, type_info = 5127}, u2 = {next = 0, 
    cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, 
    fe_iter_idx = 0, access_flags = 0, property_guard = 0, 
    extra = 0}}
//u1.v.type=7 是數組類型

(gdb) p *z.value.arr 
$27 = {gc = {refcount = 1, u = {v = {type = 7 '\a', 
        flags = 128 '\200', gc_info = 0}, type_info = 32775}}, 
  u = {v = {flags = 26 '\032', nApplyCount = 0 '\000', 
      nIteratorsCount = 0 '\000', consistency = 0 '\000'}, 
    flags = 26}, nTableMask = 4294967288, arData = 0x7ffff5a68660, 
  nNumUsed = 4, nNumOfElements = 4, nTableSize = 8, 
  nInternalPointer = 0, nNextFreeElement = 1, 
  pDestructor = 0x8c6f9c <_zval_ptr_dtor_wrapper>}
//nTableSize = 8 標識$a中數組的容量是8,   nNumUsed = 4, 已經用了4個 nNumOfElements = 4,實際用了4個, nNextFreeElement = 1,因爲$a[]=22用掉一個,默認重0開始的。

(gdb) p z.value.arr.nTableMask 
$28 = 4294967288
(gdb)p (int32_t)z.value.arr.nTableMask 
29 = -8
// nTableMask  = -nTableSize

 
(gdb) p z.value.arr.arData[0]
$30 = {val = {value = {lval = 1, dval = 4.9406564584124654e-324, 
      counted = 0x1, str = 0x1, arr = 0x1, obj = 0x1, res = 0x1, 
      ref = 0x1, ast = 0x1, zv = 0x1, ptr = 0x1, ce = 0x1, 
      func = 0x1, ww = {w1 = 1, w2 = 0}}, u1 = {v = {
        type = 4 '\004', type_flags = 0 '\000', 
        const_flags = 0 '\000', reserved = 0 '\000'}, 
      type_info = 4}, u2 = {next = 4294967295, 
      cache_slot = 4294967295, lineno = 4294967295, 
      num_args = 4294967295, fe_pos = 4294967295, 
      fe_iter_idx = 4294967295, access_flags = 4294967295, 
      property_guard = 4294967295, extra = 4294967295}}, 
  h = 9223372037048267657, key = 0x7ffff5a6db40}
//取第0個元素

(gdb) p (int32_t)$30.h | $34
$36 = -7
// nindex =  bucket.h |  (int32_t)z.value.arr.nTableMask   ,$a['foo'] 對應的值存儲的索引下標是-7
(gdb) p ((uint32_t*)(z.value.arr.arData))[(int32_t)(-7)]
$37 = 0  //算出的0是對應bucket的位置,與上邊我們取的數組的下邊一致

(gdb) p z.value.arr.arData[1]
$39 = {val = {value = {lval = 22, dval = 1.0869444208507424e-322, 
      counted = 0x16, str = 0x16, arr = 0x16, obj = 0x16, 
      res = 0x16, ref = 0x16, ast = 0x16, zv = 0x16, ptr = 0x16, 
      ce = 0x16, func = 0x16, ww = {w1 = 22, w2 = 0}}, u1 = {v = {
        type = 4 '\004', type_flags = 0 '\000', 
        const_flags = 0 '\000', reserved = 0 '\000'}, 
      type_info = 4}, u2 = {next = 4294967295, 
      cache_slot = 4294967295, lineno = 4294967295, 
      num_args = 4294967295, fe_pos = 4294967295, 
      fe_iter_idx = 4294967295, access_flags = 4294967295, 
      property_guard = 4294967295, extra = 4294967295}}, h = 0, 
  key = 0x0}
(gdb) p (int32_t)$39.h | $34
$40 = -8
(gdb) p ((uint32_t*)(z.value.arr.arData))[(int32_t)(-8)]
$41 = 2
//   2 不等於 上邊的下標1,這時說明在數組中別的key和1這個位置映射的索引數組的位置衝突,這個時候去取z.value.arr.arData[2].u2.next,一次類推直到取到和上邊的下標對應數值
(gdb) p z.value.arr.arData[2]
$42 = {val = {value = {lval = 140737314741312, 
      dval = 6.9533472301629101e-310, counted = 0x7ffff5a6dc40, 
      str = 0x7ffff5a6dc40, arr = 0x7ffff5a6dc40, 
      obj = 0x7ffff5a6dc40, res = 0x7ffff5a6dc40, 
      ref = 0x7ffff5a6dc40, ast = 0x7ffff5a6dc40, 
      zv = 0x7ffff5a6dc40, ptr = 0x7ffff5a6dc40, 
      ce = 0x7ffff5a6dc40, func = 0x7ffff5a6dc40, ww = {
        w1 = 4121353280, w2 = 32767}}, u1 = {v = {type = 6 '\006', 
        type_flags = 0 '\000', const_flags = 0 '\000', 
        reserved = 0 '\000'}, type_info = 6}, u2 = {next = 1, 
      cache_slot = 1, lineno = 1, num_args = 1, fe_pos = 1, 
      fe_iter_idx = 1, access_flags = 1, property_guard = 1, 
      extra = 1}}, h = 9223372036854953496, key = 0x1398fd0}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章