- php7源码中zval的定义是一个结构体:
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;
uint32_t type_info;
} u1;
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;
};
_zval_struct结构体由三部分组成, value是一个zend_value联合体,u1, u2是联合体,u1和u1分别占用4个字节。
接着看zend_value联合体:
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; // zval
void *ptr; // 空或不确定
zend_class_entry *ce; // 类
zend_function *func; // 函数
struct {
uint32_t w1;
uint32_t w2;
} ww;
} zend_value;
zend_value联合体可以表示整形丶浮点型等php中的类型, 甚至可以指向一个zval, 但同时只能表示其中一种类型,其占用8个字节。
_zval_struct结构体zend_val,u1,u2 8字节对齐, 所以总共占用16字节。
php虽然是弱类型语言,写代码不需要定义其类型,在底层实现的时候还是要区分类型的,底层实现做了类型转换的事情,且类型可以隐式包含了变量长度。
-
在_zval_struct结构体中的u1中包含了一个结构体v, 其中:
-
zend_uchar type
保存变量的类型,变量的类型声明如下:
-
/* regular data types */
#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
-
zend_uchar type_flags
对应变量类型特有的标记, 可以表示常量丶可被复制的类型丶需要引用计数的类型:
/* zval.u1.v.type_flags */
#define IS_TYPE_CONSTANT (1<<0) // 常量
#define IS_TYPE_REFCOUNTED (1<<2) // 引用
#define IS_TYPE_COPYABLE (1<<4) // 可复制类型
zend_uchar const_flags
是常量类型的标记。zend_uchar reserved
是保留字段。-
_zval_struct中u2中的字段解析:
- next字段用于解决哈希冲突。
- cache_slot用于运行时缓存。
- lineno记录php代码在哪一行,用于抽象语法树。
- num_args记录函数的参数个数
- fe_pos 记录foreach时的位置
- fe_iter_idx 也是用于foreach,代表游标。
- access_flags 用于类里边,表示访问修饰符。
- property_guard 用于防止类中魔术方法的循环引用。
- zval是由三个联合体所组成的,根据u1中的type来取zend_value中对应的值, 如type是long,那么直接取zend_value中的lval值。
- zval可以表示PHP中的所有变量,PHP变量使用上是弱类型的,但底层实现是区分类型的。