- 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变量使用上是弱类型的,但底层实现是区分类型的。