深入PHP变量存储的详解


Posted in PHP onJune 13, 2013

1.1.1 zval结构
Zend使用zval结构来存储PHP变量的值,该结构如下所示:

typedef union _zvalue_value {
 long lval;    /* long value */
 double dval;    /* double value */
 struct {
  char *val;
  int len;
 } str;
 HashTable *ht;    /* hash table value */
 zend_object_value obj;
} zvalue_value;

struct _zval_struct {
 /* Variable information */
 zvalue_value value;  /* value */
 zend_uint refcount;
 zend_uchar type;   /* active type */
 zend_uchar is_ref;
};
typedef struct _zval_struct zval;

Zend根据type值来决定访问value的哪个成员,可用值如下:
IS_NULL N/A
IS_LONG 对应value.lval
IS_DOUBLE 对应value.dval
IS_STRING 对应value.str
IS_ARRAY 对应value.ht
IS_OBJECT 对应value.obj
IS_BOOL 对应value.lval.
IS_RESOURCE 对应value.lval

根据这个表格可以发现两个有意思的地方:首先是PHP的数组其实就是一个HashTable,这就解释了为什么PHP能够支持关联数组了;其次,Resource就是一个long值,它里面存放的通常是个指针、一个内部数组的index或者其它什么只有创建者自己才知道的东西,可以将其视作一个handle。

1.1.2 引用计数
引用计数在垃圾收集、内存池以及字符串等地方应用广泛,Zend就实现了典型的引用计数。多个PHP变量可以通过引用计数机制来共享同一份zval,zval中剩余的两个成员is_ref和refcount就用来支持这种共享。
很明显,refcount用于计数,当增减引用时,这个值也相应的递增和递减,一旦减到零,Zend就会回收该zval。
那么is_ref呢?

1.1.3 zval状态
在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。

1.1.4 zval状态切换
当在一个zval上进行的所有赋值操作都是引用或者都是非引用时,一个is_ref就足够应付了。然而,世界总不会那么美好,PHP无法对用户进行这种限制,当我们混合使用引用和非引用赋值时,就必须要进行特别处理了。
情况I、看如下PHP代码:

<?php
$a = 1;
$b = $a;
$c = $b;
$d = &$c; // 在一堆非引用赋值中,插入一个引用
?>

这段代码首先进行了一次初始化,这将创建一个新的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变量存储的详解

情况II,看如下PHP代码:

<?php
$a = 1;
$b = &$a;
$c = &$b;
$d = $c; // 在一堆引用赋值中,插入一个非引用
?>

这段代码的前三句将把a、b和c指向一个zval,其is_ref=1, refcount=3;第四句是个非引用赋值,通常情况下只需要增加引用计数即可,然而目标zval属于引用变量,单纯的增加引用计数显然是错误的, Zend的解决办法是为d单独生成一份zval副本。
全过程如下所示:

深入PHP变量存储的详解

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

PHP 相关文章推荐
PHP nl2br函数 将换行字符转成 &amp;lt;br&amp;gt;
Aug 21 PHP
linux命令之调试工具strace的深入分析
Jun 03 PHP
php上传图片到指定位置路径保存到数据库的具体实现
Dec 30 PHP
php实现斐波那契数列的简单写法
Jul 19 PHP
php实现MySQL数据库备份与还原类实例
Dec 09 PHP
PHP使用DirectoryIterator显示下拉文件列表的方法
Mar 13 PHP
PHP的pcntl多进程用法实例
Mar 19 PHP
PHP+Mysql+jQuery查询和列表框选择操作实例讲解
Oct 22 PHP
Yii2中Restful API原理实例分析
Jul 25 PHP
关于Laravel Route重定向的一个注意点
Jan 16 PHP
PHP二维索引数组的遍历实例分析【2种方式】
Jun 24 PHP
PHP 实现base64编码文件上传出现问题详解
Sep 01 PHP
深入PHP中的HashTable结构详解
Jun 13 #PHP
基于PHP输出缓存(output_buffering)的深入理解
Jun 13 #PHP
php缓冲 output_buffering的使用详解
Jun 13 #PHP
如何在PHP中使用正则表达式进行查找替换
Jun 13 #PHP
php启用zlib压缩文件的配置方法
Jun 12 #PHP
Window下PHP三种运行方式图文详解
Jun 11 #PHP
控制PHP的输出:缓存并压缩动态页面
Jun 11 #PHP
You might like
PHP 文件上传源码分析(RFC1867)
2009/10/30 PHP
利用php操作memcache缓存的基础方法示例
2017/08/02 PHP
JQuery Tips(2) 关于$()包装集你不知道的
2009/12/14 Javascript
解决jquery的.animate()函数在IE6下的问题
2010/12/03 Javascript
js隐藏与显示回到顶部按钮及window.onscroll事件应用
2013/01/25 Javascript
javascript陷阱 一不小心你就中招了(字符运算)
2013/11/10 Javascript
Enter转换为Tab的小例子(兼容IE,Firefox)
2013/11/14 Javascript
jquery的ajax简单结构示例代码
2014/02/17 Javascript
让浏览器DOM元素最后加载的js方法
2014/07/29 Javascript
iframe里使用JavaScript控制主页转向的方法
2015/04/03 Javascript
JavaScript中的Math.LOG2E属性使用详解
2015/06/14 Javascript
异步安全加载javascript文件的方法
2015/07/21 Javascript
使用控制台破解百小度一个月只准改一次名字
2015/08/13 Javascript
jquery+css实现的红色线条横向二级菜单效果
2015/08/22 Javascript
jQuery中的siblings用法实例分析
2015/12/24 Javascript
jQuery遍历DOM元素与节点方法详解
2016/04/14 Javascript
js获取Html元素的实际宽度高度的方法
2016/05/19 Javascript
jQuery针对input的class属性写了多个值情况下的选择方法
2016/06/03 Javascript
js学习之----深入理解闭包
2016/11/21 Javascript
浅析Node.js:DNS模块的使用
2016/11/23 Javascript
使用webpack3.0配置webpack-dev-server教程
2018/05/29 Javascript
Vue.js 实现数据展示全部和收起功能
2018/09/05 Javascript
PHPStorm中如何对nodejs项目进行单元测试详解
2019/02/28 NodeJs
JS数组方法reduce的用法实例分析
2020/03/03 Javascript
element-ui 弹窗组件封装的步骤
2021/01/22 Javascript
Python MD5文件生成码
2009/01/12 Python
Python实现包含min函数的栈
2016/04/29 Python
python中不能连接超时的问题及解决方法
2018/06/10 Python
基于Python的Post请求数据爬取的方法详解
2019/06/14 Python
在python中利用dict转json按输入顺序输出内容方式
2020/02/27 Python
Python+Selenium随机生成手机验证码并检查页面上是否弹出重复手机号码提示框
2020/09/21 Python
python urllib和urllib3知识点总结
2021/02/08 Python
一款恶搞头像特效的制作过程 利用css3和jquery
2014/11/21 HTML / CSS
HTML最新标准HTML5总结(必看)
2016/06/13 HTML / CSS
大学生家政服务项目创业计划书
2014/01/30 职场文书
党的群众路线教育实践活动总结报告
2014/07/03 职场文书