深入PHP变量存储的详解

 更新时间:2013年06月13日 10:56:15   投稿:jingxian  
本篇文章是对PHP变量的存储进行了详细的分析介绍,需要的朋友参考下

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状态
可见,有必要指出当前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;
?>

这段代码首先进行了一次初始化,这将创建一个新的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也是受保护的对象。
整个过程图示如下:

情况II,看如下PHP代码:

复制代码 代码如下:

<?php
$a = 1;
$b = &$a;
$c = &$b;
?>

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

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

相关文章

  • PHP递归调用的小技巧讲解

    PHP递归调用的小技巧讲解

    对于初学PHP语言的朋友来说,可能对PHP递归的用法还是比较陌生。我们今天就来讲一下有关PHP递归调用实现多元数组替换功能
    2013-02-02
  • PHP安全上传图片的方法

    PHP安全上传图片的方法

    这篇文章主要介绍了PHP安全上传图片的方法,可检测图片类型实现安全判断图片的功能,非常具有实用价值,需要的朋友可以参考下
    2015-03-03
  • 关于php支持分块与断点续传文件下载功能代码

    关于php支持分块与断点续传文件下载功能代码

    一篇关于php流下载,就是可以支持分块与断点续传文件下载,有需要的朋友可以看看
    2014-05-05
  • apache和php之间协同工作的配置经验分享

    apache和php之间协同工作的配置经验分享

    闲暇之时也会对PHP知识做一些研究与大家共享,首先工欲善其事,必先利其器,可能比喻不是很恰当,php也需要一系列的环境配置,以下是我配置php的一些手稿,经测试已成功运行了
    2013-04-04
  • php采集时被封ip的解决方法

    php采集时被封ip的解决方法

    最近各种网站的采集程序写的比较多,遇到在采某网站时采到100多条时突然发现对方的网站打不开了,猜到肯定被封ip了,用了代理还是会封,这不是办法。
    2010-08-08
  • 通过PHP修改Linux或Unix口令的方法分享

    通过PHP修改Linux或Unix口令的方法分享

    本文介绍如何使用PHP脚本修改Linux或Unix系统口令,需要的朋友可以参考下
    2012-01-01
  • php文件上传的两种实现方法

    php文件上传的两种实现方法

    这篇文章主要为大家详细介绍了两种php文件上传的实现方法,感兴趣的朋友可以参考一下
    2016-04-04
  • PHP CKEditor 上传图片实现代码

    PHP CKEditor 上传图片实现代码

    CKEditor的原包中没有包含图片的上传服务器端处理文件,其公司的另一款开源产品:CKFinder做了很好的补充。但是要下载这个源代码再进行配置,虽然方便了很多,但是仅仅为了上传图片,却要使用这么大的整个系统来使用,确实有点大材小用。
    2009-11-11
  • 详解php反序列化

    详解php反序列化

    这篇文章主要介绍了php反序列化的相关知识,文中讲解非常细致,代码帮助各位更好的理解和学习,感兴趣的朋友可以了解下
    2020-06-06
  • 配置eAccelerator和XCache扩展来加速PHP程序的执行

    配置eAccelerator和XCache扩展来加速PHP程序的执行

    这篇文章主要介绍了配置eAccelerator和XCache扩展来加速PHP程序的执行的方法,XCache和PHP5.5以来自带的Zend Opcache一样都是在共享内存中存储Opcode的缓存器,需要的朋友可以参考下
    2015-12-12

最新评论