php 获取变量指针_PHP7-变量的内部实现

01a84e7e04d742930f39f08bb93cf47c.png

变量的基础结构

//zend_types.htypedef struct _zval_struct zval;typedef union _zend_value { zend_long lval; //int整形 double dval; //浮点型 zend_refcounted *counted; zend_string *str; //string字符串 zend_array *arr; //array数组 zend_object *obj; //object对象 zend_resource *res; //resource资源类型 zend_reference *ref; //引用类型,通过&$var_name定义的 zend_ast_ref *ast; //下面几个都是内核使用的value zval *zv; void *ptr; zend_class_entry *ce; zend_function *func; struct { uint32_t w1; uint32_t w2; } ww;} zend_value;struct _zval_struct { zend_value value; //变量实际的value union { struct { ZEND_ENDIAN_LOHI_4( //这个是为了兼容大小字节序,小字节序就是下面的顺序,大字节序则下面4个顺序翻转 zend_uchar type, //变量类型 zend_uchar type_flags, //类型掩码,不同的类型会有不同的几种属性,内存管理会用到 zend_uchar const_flags, zend_uchar reserved) //call info,zend执行流程会用到 } v; uint32_t type_info; //上面4个值的组合值,可以直接根据type_info取到4个对应位置的值 } u1; union { uint32_t var_flags; uint32_t next; //哈希表中解决哈希冲突时用到 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 */ } u2; //一些辅助值};

zval结构比较简单,内嵌一个union类型的zend_value保存具体变量类型的值或指针,zval中还有两个union:u1、u2:

u1: 它的意义比较直观,变量的类型就通过u1.v.type区分,另外一个值type_flags为类型掩码,在变量的内存管理、gc机制中会用到(之前分享的垃圾回收机制中,变量的type_flags只有包含IS_TYPE_COLLECTABLE的变量才会被GC收集)

u2: 这个值纯粹是个辅助值,假如zval只有:value、u1两个值,整个zval的大小也会对齐到16byte,既然不管有没有u2大小都是16byte,把多余的4byte拿出来用于一些特殊用途还是很划算的,比如next在哈希表解决哈希冲突时会用到,还有fe_pos在foreach会用到......

从zend_value可以看出,除long、double类型直接存储值外,其它类型都为指针,指向各自的结构。

类型

标量类型

最简单的类型是true、false、long、double、null,其中true、false、null没有value,直接根据type区分,而long、double的值则直接存在value中:zend_long、double,也就是标量类型不需要额外的value指针。

字符串

PHP中字符串通过zend_string表示:

struct _zend_string { zend_refcounted_h gc; zend_ulong h; /* hash value */ size_t len; char val[1];};

gc: 变量引用信息,比如当前value的引用数,所有用到引用计数的变量类型都会有这个结构,3.1节会详细分析

h: 哈希值,数组中计算索引时会用到

len: 字符串长度,通过这个值保证二进制安全

val: 字符串内容,变长struct,分配时按len长度申请内存

数组

array是PHP中非常强大的一个数据结构,它的底层实现就是普通的有序HashTable,这里简单看下它的结构。

typedef struct _zend_array HashTable;struct _zend_array { zend_refcounted_h gc; //引用计数信息,与字符串相同 union { struct { ZEND_ENDIAN_LOHI_4( zend_uchar flags, zend_uchar nApplyCount, zend_uchar nIteratorsCount, zend_uchar reserve) } v; uint32_t flags; } u; uint32_t nTableMask; //计算bucket索引时的掩码 Bucket *arData; //bucket数组 uint32_t nNumUsed; //已用bucket数 uint32_t nNumOfElements; //已有元素数,nNumOfElements <= nNumUsed,因为删除的并不是直接从arData中移除 uint32_t nTableSize; //数组的大小,为2^n uint32_t nInternalPointer; //数值索引 zend_long nNextFreeElement; dtor_func_t pDestructor;};对象/资源struct _zend_object { zend_refcounted_h gc; uint32_t handle; zend_class_entry *ce; //对象对应的class类 const zend_object_handlers *handlers; HashTable *properties; //对象属性哈希表 zval properties_table[1];};struct _zend_resource { zend_refcounted_h gc; int handle; int type; void *ptr;};

引用

在PHP中通过&操作符产生一个引用变量,也就是说不管以前的类型是什么,&首先会创建一个zend_reference结构,其内嵌了一个zval,这个zval的value指向原来zval的value(如果是布尔、整形、浮点则直接复制原来的值),然后将原zval的类型修改为IS_REFERENCE,原zval的value指向新创建的zend_reference结构。

struct _zend_reference { zend_refcounted_h gc; zval val;};

结构非常简单,除了公共部分zend_refcounted_h外只有一个val,举个示例看下具体的结构关系:

$a = "time:" . time(); //$a -> zend_string_1(refcount=1)$b = &$a; //$a,$b -> zend_reference_1(refcount=2) -> zend_string_1(refcount=1)

最终的结果如图:

0a1d26908ca92cc97a5cf56808162863.png

image.png

注意:引用只能通过&产生,无法通过赋值传递,比如:

$a = "time:" . time(); //$a -> zend_string_1(refcount=1)$b = &$a; //$a,$b -> zend_reference_1(refcount=2) -> zend_string_1(refcount=1)$c = $b; //$a,$b -> zend_reference_1(refcount=2) -> zend_string_1(refcount=2) //$c -> ---

b = &a这时候a、b的类型是引用,但是c = b并不会直接将b赋值给c,而是把b实际指向的zval赋值给c,如果想要$c也是一个引用则需要这么操作:

$a = "time:" . time(); //$a -> zend_string_1(refcount=1)$b = &$a; //$a,$b -> zend_reference_1(refcount=2) -> zend_string_1(refcount=1)$c = &$b;/*或$c = &$a*/ //$a,$b,$c -> zend_reference_1(refcount=3) -> zend_string_1(refcount=1) 

这个也表示PHP中的引用只可能有一层 ,不会出现一个引用指向另外一个引用的情况 。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值