小而巧的zval

zval 是一个结构体,其中包含三个联合体(共同体):value、u1、u2;vaule8 字节,u1 和 u2 都是 4 个字节,正好 8 字节对其,所以 zval 的大小是 16 字节。

zval 用 16 字节可以表示 PHP 中的任意一个变量。

如何表示的呢?

line84 typedef struct _zval_struct zval; 为_zval_struct 这个结构体定义别名为 zval, ctrl + ] 跳转到 line121 查看_zval_struct:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct _zval_struct {
zend_value value; /* value */
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar type, /* active type */ // unsigned char 占用一个字节,v这个结构体中有四个,所以v占用4个字节
zend_uchar type_flags,
zend_uchar const_flags,
zend_uchar reserved) /* call info for EX(This) */
} v;
uint32_t type_info; // unsigned int 占用4个字节
} u1; // v四个字节, type_info也是四个字节,所以u1占用4个字节
union {
uint32_t next; /* hash collision chain */
uint32_t cache_slot; /* literal cache slot */
uint32_t lineno; /* line number (for ast nodes) */
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; // 所有的值都是 unsigned int 所以u2占用4个字节
};

然后定位 zend_value, ctrl+]跳转:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
typedef union _zend_value {
zend_long lval; /* long value 64位 8字节*/
double dval; /* double value 64位 8字节*/
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; /* 显然4字节 */
} zend_value;

接下来我们详细说明一下 zval 的结构: 显然, 它是一个结构体,包含三个值, 其类型都是联合体。

先看 value 的这个联合体 zend_value, 包含 14 个值, 经过 tags 的 ctrl+]跳转,我们发现:

  1. zend_long 是 int64_t 的别名, 而 int64_t 又是__int64 的别名(关于__int64 可以查看笔记: 备忘/bit B(byte) KB.md)。

  2. zend_refcounted 是_zend_refcounted 的别名,而_zend_refcounted 是一个结构体,其中只有一个值 gc,gc 是 zend_refcounted_h 类型的数据,而 zend_refcounted_h 是一个结构体:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    typedef struct _zend_refcounted_h {
    uint32_t refcount; /* reference counter 32-bit */
    union {
    struct {
    ZEND_ENDIAN_LOHI_3(
    zend_uchar type,
    zend_uchar flags, /* used for strings & objects */
    uint16_t gc_info) /* keeps GC root number (or 0) and color */
    } v;
    uint32_t type_info;
    } u;
    } zend_refcounted_h;

    uint32_t 是 unsigned int 的别名。refcount 表示。。。,u 这个联合体中包含一个结构体 v 和一个 32 位正整型 type_info。

    zend_uchar 是 unsigned char 的别名。

    。。。未完待续

  3. str 是指针,zend_string 是_zend_string 的别名

    1
    2
    3
    4
    5
    6
    struct _zend_string {
    zend_refcounted_h gc;
    zend_ulong h; /* hash value */
    size_t len;
    char val[1];
    };

    gc 和垃圾回收有关。zend_ulong 是 uint32_t 的别名,表示 hash 值,防止冲突。

    关于 size_t:

    size_t 是一些 C/C++标准在 stddef.h 中定义的。这个类型足以用来表示对象的大小。size_t 的真实类型与操作系统有关。

    在 32 位架构中被普遍定义为:typedef unsigned int size_t;

    而在 64 位架构中被定义为:typedef unsigned long size_t;

    为什么有时候不用 int,而是用 size_type 或者 size_t: 与 int 固定四个字节不同有所不同,size_t 的取值 range 是目标平台下最大可能的数组尺寸,一些平台下 size_t 的范围小于 int 的正数范围,又或者大于 unsigned int. 使用 Int 既有可能浪费,又有可能范围不够大。

    len 用来储存字符串的长度。

    val 是一个柔性数组,用来储存字符串。

  4. arr 是指针,zend_array 是_zend_array 的别名

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    struct _zend_array {
    zend_refcounted_h 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;
    Bucket *arData;
    uint32_t nNumUsed;
    uint32_t nNumOfElements;
    uint32_t nTableSize;
    uint32_t nInternalPointer;
    zend_long nNextFreeElement;
    dtor_func_t pDestructor;
    };

    gc 与垃圾回收有关。

    联合体 v。。。

    。。。未完待续

  5. obj 是指针,zend_object 是_zend_object 的别名

    1
    2
    3
    4
    5
    6
    7
    8
    struct _zend_object {
    zend_refcounted_h gc;
    uint32_t handle; // TODO: may be removed ???
    zend_class_entry *ce;
    const zend_object_handlers *handlers;
    HashTable *properties;
    zval properties_table[1];
    };

    。。。未完待续

  6. res 是指针,zend_resource 是_zend_resource 的别名

    1
    2
    3
    4
    5
    6
    struct _zend_resource {
    zend_refcounted_h gc;
    int handle; // TODO: may be removed ???
    int type;
    void *ptr;
    };

    。。。未完待续

  7. ref 是指针,zend_reference 是_zend_reference 的别名

    1
    2
    3
    4
    struct _zend_reference {
    zend_refcounted_h gc;
    zval val;
    };

    。。。未完待续

  8. ast 是指针,zend_ast_ref 是_zend_ast_ref 的别名

    1
    2
    3
    4
    struct _zend_ast_ref {
    zend_refcounted_h gc;
    zend_ast *ast;
    };

    zend_ast 是_zend_ast 的别名

    1
    2
    3
    4
    5
    6
    7
    8
    9
    typedef uint16_t zend_ast_kind;
    typedef uint16_t zend_ast_attr;

    struct _zend_ast {
    zend_ast_kind kind; /* Type of the node (ZEND_AST_* enum constant) */
    zend_ast_attr attr; /* Additional attribute, use depending on node type */
    uint32_t lineno; /* Line number */
    zend_ast *child[1]; /* Array of children (using struct hack) */
    };

    。。。未完待续

  9. zv 是指针

    。。。未完待续

  10. ptr 是指针

    。。。未完待续

  11. ce 是指针,zend_class_entry 是_zend_class_entry 的别名

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    struct _zend_class_entry {
    char type;
    zend_string *name;
    struct _zend_class_entry *parent;
    int refcount;
    uint32_t ce_flags;

    int default_properties_count;
    int default_static_members_count;
    zval *default_properties_table;
    zval *default_static_members_table;
    zval *static_members_table;
    HashTable function_table;
    HashTable properties_info;
    HashTable constants_table;

    union _zend_function *constructor;
    union _zend_function *destructor;
    union _zend_function *clone;
    union _zend_function *__get;
    union _zend_function *__set;
    union _zend_function *__unset;
    union _zend_function *__isset;
    union _zend_function *__call;
    union _zend_function *__callstatic;
    union _zend_function *__tostring;
    union _zend_function *__debugInfo;
    union _zend_function *serialize_func;
    union _zend_function *unserialize_func;

    zend_class_iterator_funcs iterator_funcs;

    /* handlers */
    zend_object* (*create_object)(zend_class_entry *class_type);
    zend_object_iterator *(*get_iterator)(zend_class_entry *ce, zval *object, int by_ref);
    int (*interface_gets_implemented)(zend_class_entry *iface, zend_class_entry *class_type); /* a class implements this interface */
    union _zend_function *(*get_static_method)(zend_class_entry *ce, zend_string* method);

    /* serializer callbacks */
    int (*serialize)(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data);
    int (*unserialize)(zval *object, zend_class_entry *ce, const unsigned char *buf, size_t buf_len, zend_unserialize_data *data);

    uint32_t num_interfaces;
    uint32_t num_traits;
    zend_class_entry **interfaces;

    zend_class_entry **traits;
    zend_trait_alias **trait_aliases;
    zend_trait_precedence **trait_precedences;

    union {
    struct {
    zend_string *filename;
    uint32_t line_start;
    uint32_t line_end;
    zend_string *doc_comment;
    } user;
    struct {
    const struct _zend_function_entry *builtin_functions;
    struct _zend_module_entry *module;
    } internal;
    } info;
    };

    。。。未完待续

  12. func 是指针

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    union _zend_function {
    zend_uchar type; /* MUST be the first element of this struct! */
    uint32_t quick_arg_flags;

    struct {
    zend_uchar type; /* never used */
    zend_uchar arg_flags[3]; /* bitset of arg_info.pass_by_reference */
    uint32_t fn_flags;
    zend_string *function_name;
    zend_class_entry *scope;
    union _zend_function *prototype;
    uint32_t num_args;
    uint32_t required_num_args;
    zend_arg_info *arg_info;
    } common;

    zend_op_array op_array;
    zend_internal_function internal_function;
    };

    。。。未完待续

  13. ww 结构体

综上可以看出,value 中存储的是变量内容,接下来我们看两个联合体 u1 和 u2。

1
2
3
4
5
6
7
8
9
10
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;
uint32_t type_info;
} u1;

u1 中有一个结构体 v 和一个 32 位整型 type_info,这个 type_info 的作用就是获取 v 的值。这么写是一个小技巧。

zend_uchar 是 unsigned char 的别名,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

以上,整型就是 IS_LONG。

。。。未完待续

1
2
3
4
5
6
7
8
9
10
11
union {
uint32_t next; /* hash collision chain */
uint32_t cache_slot; /* literal cache slot */
uint32_t lineno; /* line number (for ast nodes) */
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;

u2 中的 next 用来解决数组中的冲突。