php变量存储结构

    一直不太理解,php的弱类型从何而来,今天看了之后,终于感觉明白了。于zend而言,其面对的数据都是zval类型的。那么zval是什么样的呢?

在PHP中,所有的变量都是用一个结构-zval来保存的, 在Zend/zend.h中我们可以看到zval的定义:

php变量存储结构_第1张图片

其中zvalue_value是真正保存数据的关键部分,定义为一个联合体(union),可以用debug_zval_dump函数来佐证。

php变量存储结构_第2张图片

PHP中常见的变量类型有:

1.整形/浮点/长整型/bool值,等
2.字符串
3.数组
4.对象
5.资源

PHP根据zval中的type字段来储存一个变量的真正类型,然后根据type来选择如何获取zvalue_value的值

php变量存储结构_第3张图片

比较特别的是资源,在PHP中,资源是个很特别的变量,任何不属于PHP内建的变量类型的变量,都会被看作成资源来进行保存,比如,数据库句柄,打开的文件句柄等等。 对于资源:
这个时候,会去取zval.value.lval, 此时的lval是个整型的指示器, 然后PHP会再根据这个指示器在PHP内建的一个资源列表中查询相对应的资源,此时的lval就好像是对应于资源链表的偏移值。

这里写图片描述

借用这样的机制,PHP就实现了弱类型,因为对于ZEND的来说,它所面对的永远都是同一种类型,那就是zval。

refcount参数


如上面所示,PHP试着在变量拷贝(如 $b = $a )的时候变得聪明些。“=”也称为赋值操作符。当进行赋值操作时,Zend引擎不会创建一个新的变量窗口,而是增大变量窗口的 refcount 字段,这个变量小的时候还不太明显,但是你可以想象一下,当这个变量是一个巨大的字符串或一个巨大 的数组时,这将节约多少的内存。

php变量存储结构_第4张图片

变量a,包含文本”this is”。默认情况下,引用计数(refcount)等于1

php变量存储结构_第5张图片

后面的赋值操作,并不会创建新的变量窗口,而是增大refcount的值

php变量存储结构_第6张图片

如果$c的值发生了改变
根据refcount的值的不同,它会有两种不同的处理方式。如果 refcount等于1,这个变量容器将更新它的值(也许同时会更新它的类型)。如果refcount大于1,将创建一个包含了新值(和类型)的变量容 器。如上图所示,$a变量所在的变量容器的refcount值被减去一,现在refcount的值是2,而新创建的容器的refcount的值为 1。

这里写图片描述

当对一个变量使用unset函数时,这个变量所在的容器的refcount值将减去一,如图上图所示。如果refcount的值少于1, Zend引 擎将翻译这个变量容器,这个变量容器将会被销毁

is_ref参数

   在PHP中,变量有两种——引用和非引用的,它们在Zend中都是采用引用计数的方式存储的。对于非引用型变量,要求变量间互不相干,修改一个变量时,不能影响到其他变量,采用Copy-On-Write机制即可解决这种冲突——当试图写入一个变量时,Zend若发现该变量指向的zval被多个变量共享,则为其复制一份refcount为1的zval,并递减原zval的refcount,这个过程称为“zval分离”。然而,对于引用型变量,其要求和非引用型相反,引用赋值的变量间必须是捆绑的,修改一个变量就修改了所有捆绑变量。

   可见,有必要指出当前zval的状态,以分别应对这两种情况,is_ref就是这个目的,它指出了当前所有指向该zval的变量是否是采用引用赋值的——要么全是引用,要么全不是。此时再修改一个变量,只有当发现其zval的is_ref为0,即非引用时,Zend才会执行Copy-On-Write。


这段代码首先进行了一次初始化,这将创建一个新的zval,is_ref=0, refcount=1,并将a指向这个zval;之后是两次非引用赋值,正如前面所说,只要把b和c都指向a的zval即可;最后一行是个引用赋值,需要is_ref为1,但是Zend发现c指向的zval并不是引用型的,于是为c创建单独的zval,并同时将d指向该zval。
从本质上来说,这也可以看作是一种Copy-On-Write,不仅仅是value,is_ref也是受保护的对象。

php变量存储结构_第7张图片

参数传递

PHP函数参数的传递和变量赋值是一样的,非引用传递相当于非引用赋值,引用传递相当于引用赋值,并且也有可能会导致执行zval状态切换。

参考链接

链接1(推荐)
链接2
链接3(推荐)

你可能感兴趣的:(php)