本文最后更新于: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;
union {
struct {
ZEND_ENDIAN_LOHI_4 (
zend_uchar type,
zend_uchar type_flags,
zend_uchar const_flags,
zend_uchar reserved)
} v;
uint32_t type_info;
} u1;
union {
uint32_t next;
uint32_t cache_slot;
uint32_t lineno;
uint32_t num_args;
uint32_t fe_pos;
uint32_t fe_iter_idx;
uint32_t access_flags;
uint32_t property_guard;
uint32_t extra;
} u2;
} ;
然后定位 zend_value, ctrl+]跳转:
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;
void * ptr;
zend_class_entry * ce;
zend_function * func;
struct {
uint32_t w1;
uint32_t w2;
} ww;
} zend_value;
接下来我们详细说明一下 zval 的结构: 显然, 它是一个结构体,包含三个值, 其类型都是联合体。
先看 value 的这个联合体 zend_value, 包含 14 个值, 经过 tags 的 ctrl+]跳转,我们发现:
zend_long 是 int64_t 的别名, 而 int64_t 又是__int64 的别名(关于__int64 可以查看笔记: 备忘/bit B(byte) KB.md)。
zend_refcounted 是_zend_refcounted 的别名,而_zend_refcounted 是一个结构体,其中只有一个值 gc,gc 是 zend_refcounted_h 类型的数据,而 zend_refcounted_h 是一个结构体:
typedef struct _zend_refcounted_h {
uint32_t refcount;
union {
struct {
ZEND_ENDIAN_LOHI_3 (
zend_uchar type,
zend_uchar flags,
uint16_t gc_info)
} v;
uint32_t type_info;
} u;
} zend_refcounted_h;
uint32_t 是 unsigned int 的别名。refcount 表示。。。,u 这个联合体中包含一个结构体 v 和一个 32 位正整型 type_info。
zend_uchar 是 unsigned char 的别名。
。。。未完待续
str 是指针,zend_string 是_zend_string 的别名
struct _zend_string {
zend_refcounted_h gc;
zend_ulong h;
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 是一个柔性数组,用来储存字符串。
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。。。
。。。未完待续
obj 是指针,zend_object 是_zend_object 的别名
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 ] ;
} ;
。。。未完待续
res 是指针,zend_resource 是_zend_resource 的别名
struct _zend_resource {
zend_refcounted_h gc;
int handle;
int type;
void * ptr;
} ;
。。。未完待续
ref 是指针,zend_reference 是_zend_reference 的别名
struct _zend_reference {
zend_refcounted_h gc;
zval val;
} ;
。。。未完待续
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;
zend_ast_attr attr;
uint32_t lineno;
zend_ast * child[ 1 ] ;
} ;
。。。未完待续
zv 是指针
。。。未完待续
ptr 是指针
。。。未完待续
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;
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) ;
union _zend_function * ( * get_static_method) ( zend_class_entry * ce, zend_string* method) ;
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;
} ;
。。。未完待续
func 是指针
union _zend_function {
zend_uchar type;
uint32_t quick_arg_flags;
struct {
zend_uchar type;
zend_uchar arg_flags[ 3 ] ;
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;
} ;
。。。未完待续
ww 结构体
综上可以看出,value 中存储的是变量内容,接下来我们看两个联合体 u1 和 u2。
union {
struct {
ZEND_ENDIAN_LOHI_4 (
zend_uchar type,
zend_uchar type_flags,
zend_uchar const_flags,
zend_uchar reserved)
} 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;
uint32_t cache_slot;
uint32_t lineno;
uint32_t num_args;
uint32_t fe_pos;
uint32_t fe_iter_idx;
uint32_t access_flags;
uint32_t property_guard;
uint32_t extra;
} u2;
u2 中的 next 用来解决数组中的冲突。