变量 php是如何统一实现各种变量类型的? 这和php变量类型的实现是密不可分的。变量名``变量类型``变量值,他们在php变量类型实现中主要对应 zval zend_value 和
变量结构体 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct  _zval_struct {
	zend_value        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      var_flags;
		uint32_t      next;                 
		uint32_t      cache_slot;           
		uint32_t      lineno;               
		uint32_t      num_args;             
		uint32_t      fe_pos;               
		uint32_t      fe_iter_idx;          
	} u2;
};
变量类型 其中type 表示当前变量的类型,例如string array int等,以下为php中全部变量类型。1
typedef  unsigned  char  zend_uchar;
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;				
    	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;
在这些类型里不存在boolean类型,因为在php7中已经将boolean拆分为true和false.直接保存在type中,也就是说 
从上边的结构体可以看出来 zend_long、double 类型不是指针类型,也就是说整形和浮点型正能进行深拷贝,引用计数和写时复制。因为有引用计数和写时复制在变量赋值且不做修改时 
 
以string类型为例,它在php中的结构类型应该是这样的:
_zend_string:1
2
3
4
5
6
struct  _zend_string {
	zend_refcounted_h gc;
	zend_ulong        h;                
	size_t             len;
	char               val[1 ];          
};
其中gc 字段表示这个结构体被引用的次数,垃圾回收的时候会用到。 
h 字符串通过Times33计算出来的hashcode 
len 字符串长度 
val 字符串内容 
 
我们在这里都会有疑问,为什么存储字符串内容的时候没有使用char*类型,而用了一个数组类型来存储。zend_string
zend_string结构在内存中如下所示:
当时也在char val[1]的地方疑惑了很久,再往上找了很多文章,其中
1
如果你对 C 语言了解的不是很深入的话,可能会觉得 val 的定义有些奇怪:这个声明只有一个元素,但是显然我们想存储的字符串长度肯定大于一个字符的长度。这里其实使用的是结构体的一个『黑』方法:在声明数组时只定义一个元素,但是实际创建 zend_string 时再分配足够的内存来存储整个字符串。这样我们还是可以通过 val 访问完整的字符串。
参考: