小而巧的zval

本文最后更新于:2023年12月5日 晚上

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:

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+]跳转:

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 是一个结构体:

    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 的别名

    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 的别名

    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 的别名

    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 的别名

    struct _zend_resource {
        zend_refcounted_h gc;
        int               handle; // TODO: may be removed ???
        int               type;
        void             *ptr;
    };

    。。。未完待续

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

    struct _zend_reference {
        zend_refcounted_h gc;
        zval              val;
    };

    。。。未完待续

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

    struct _zend_ast_ref {
        zend_refcounted_h gc;
        zend_ast         *ast;
    };

    zend_ast 是_zend_ast 的别名

    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 的别名

    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 是指针

    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。

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。

。。。未完待续

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 用来解决数组中的冲突。


小而巧的zval
http://blog.lujinkai.cn/PHP/源码/小而巧的zval/
作者
像方便面一样的男子
发布于
2020年12月9日
更新于
2023年12月5日
许可协议