1.zval是php變量底層定義的結構,一共16字節
typedef struct _zval_struct zval;
struct _zval_struct {
zend_value value;
union u1;
union u2;
};
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}