ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
### 变量的内部实现 - 变量有两个组成部分,变量名(zval),变量值(zend_value)。变量之间的传递,赋值通常也是针对zend_value操作的。 ### zval 的基础结构 ``` typedef struct _zval_struct zval; struct _zval_struct { zend_value value; u1; u2; } // value保存具有变量类型的值或指针。 // value的结构 typedef union _zend_value { zend_long lval; // int 整型(值) double dval; // 浮点型(值) zend_string *str; // string字符串型(指针) zend_array *arr; // array数组(指针) zend_object *obj; // object对象(指针) zend_resource *res; // resource资源(指针) zend_reference *ref; // 引用类型(指针) ... } value; // u1 的结构 union { struct { zend_uchar type, // 变量类型;若变量是true,false,null,则type直接表示值。其他类型则通过u1.type和value作对应,找到变量的值 zend_uchar type_flags, zend_uchar const_flags, zend_uchar reserved } v; uint32_t type_info; } u1; // u2的结构 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 ``` ### zend_value 的结构 - 整型和浮点型 在变量赋值时,是直接复制 ``` $a = 10 $b = $a 直接把$a所指向的zval复制一份给$b ``` - zend_string 字符串 二进制安全的,通过len变量来实现 ``` // zend_string的结构 struct _zend_string { zend_refcounted_h gc; // 变量引用信息 zend_ulong h; // 哈希值,数组中计算索引时会用到 size_t len; // 字符串长度,通过这个值保证了二进制安全 char val[1] // 字符串内容 } // zend_string 写时复制 $a = 'string'.date('Y-m-d'); $b = $a; // 当把$a赋值给$b时,其实在内存中$b 和 $a 都是指向同一个zend_string $b = 'hello' // 当$b重新赋值时,这时候发生变化(写时复制).将zend_string复制一份, // 变成两个zend_string.分别$a指向一个,$b指向一个。$b重新赋值时,只改变 // $b指向的zend_string,并不影响$a. ``` - zend_reference引用 引用是PHP中比较特殊的一种类型,它实际是指向另外一个PHP变量,对它的修改会直接改动实际指向的zval,可以简单的理解为C中的指针,在PHP中通过`&`操作符产生一个引用变量,也就是说不管以前的类型是什么,`&`首先会创建一个`zend_reference`结构,其内嵌了一个zval,这个zval的value指向原来zval的value(如果是布尔、整形、浮点则直接复制原来的值),然后将原zval的类型修改为IS\_REFERENCE,原zval的value指向新创建的`zend_reference`结构。 ``` struct _zend_reference { zend_refcounted gc; zval val; } 举例1 $a = "time:" . time(); //$a -> zend_string.gc.refcount=1 $b = &$a; //$a,$b -> zend_reference.gc.refcount=2 -> zend_string.gc.refcount=1 举例2 $a = "time:" . time(); //$a -> zend_string.gc.refcount=1 $b = &$a; //$a,$b -> zend_reference.gc.refcount=2 -> zend_string.gc.refcount=1 $c = $b; //$a,$b -> zend_reference.gc.refcount=2 -> zend_string.gc.refcount=2 //$c->---------------------------------------------------⬆ //`$b = &$a`这时候`$a`、`$b`的类型是引用,但是`$c = $b`并不会直接将`$b`赋值给`$c`,而是把`$b`实际指向的zval赋值给`$c` 举例3 $a = "time:" . time(); //$a -> zend_string.gc.refcount=1 $b = &$a; //$a,$b -> zend_reference.gc.refcount=2 -> zend_string.gc.refcount=1 $c = &$b;/*或$c = &$a*/ //$a,$b,$c -> zend_reference.gc.refcount=3 -> zend_string.gc.refcount=1 ``` - zend_array数组 详细介绍:https://www.kancloud.cn/nickbai/php7/363268 ``` // zend_array 的内部结构 struct _zend_array{ zend_refcounted gc; // gc 用于记录引用信息 union { ... } u; uint32_t nTableMask; // 数组的key做hash运算后和nTableMask做与运算,计算出的值就是就是bucket负数索引的位置 Bucket *arData; // 数组中 key-value存储的地方 uint32_t nNumUsed; // 占用的bucket的个数 uint32_t nNumOfElements;// 实际用到的bucket的个数 uint32_t nTableSize; // 数组的大小 uint32_t nInternalPointer; zend_long nNextFreeElement; // 下一个无key 插入时,默认的key } // Bucket 的结构 typedef struct _Bucket { zval val; // 数组value的zval zend_long h; // key做hash运算后的值 zend_string *key // 数组的key } Bucket ```