Press "Enter" to skip to content

深入理解PHP原理之变量(Variables inside PHP)

或许你知道,或许你不知道,PHP是一个弱类型,动态的脚本语言。所谓弱类型,就是说PHP并不严格验证变量类型(严格来讲,PHP是一个中强类型语言,这部分内容会在以后的文章中叙述),在申明一个变量的时候,并不需要显示指明它保存的数据的类型:

<?php
  $var = 1; //int
  $var = "laruence"; //string
  $var = 1.0002; //float
  $var = array(); // array
  $var = new Exception('error'); //object;

动态语言,就是说,PHP的语言结构在运行期是可以改变的,比如我们在运行期require一个函数定义文件,从而导致语言的函数表动态的改变。
所谓脚本语言,就是说,PHP并不是独立运行的,要运行PHP我们需要PHP解析器:

  /usr/bin/php -f example.php

我前面的文章中已经讲过,PHP的执行是通过Zend engine(ZE, Zend引擎), ZE是用C编写的,大家都知道C是一个强类型语言,也就是说,在C中所有的变量在它被声明到最终销毁,都只能保存一种类型的数据。 那么PHP是如何在ZE的基础上实现弱类型的呢?

首先要声明一点,如果你以前没有接触过PHP的源码分析,扩展开发。 如果你并不了解PHP的架构, 没有听说ZE,那么我建议你先看看我前面的文章,尤其推荐:

  • 深入浅出PHP(PHP Internals)
  • 深入理解PHP原理之Opcodes
  • 在PHP中,所有的变量都是用一个结构-zval来保存的, 在Zend/zend.h中我们可以看到zval的定义:

      typedef struct _zval_struct {
        zvalue_value value;
        zend_uint refcount;
        zend_uchar type;
        zend_uchar is_ref;
      } zval;
     

    其中zvalue_value是真正保存数据的关键部分,现在到了揭晓谜底的时候了,PHP是如何在ZE的基础上实现弱类型的呢? 因为zvalue_value是个联合体(union),

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

    那么这个结构是如何储存PHP中的多种类型的呢?
    PHP中常见的变量类型有:

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

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

       zval.type = IS_LONG;//整形
       zval.type = IS_BOOL;//布尔值
    

    就去取zval.value.lval,对于bool值来说lval∈(0|1);
    如果是双精度,或者float则会去取zval.value的dval。
    而如果是字符串,那么:

       zval.type = IS_STRING
    

    这个时候,就会取:
    zval.value.str
    而这个也是个结构,存有C分格的字符串和字符串的长度。
    而对于数组和对象,则type分别对应IS_ARRAY, IS_OBJECT, 相对应的则分别取zval.value.ht和obj
    比较特别的是资源,在PHP中,资源是个很特别的变量,任何不属于PHP内建的变量类型的变量,都会被看作成资源来进行保存,比如,数据库句柄,打开的文件句柄等等。 对于资源:

       type = IS_RESOURCE
    

    这个时候,会去取zval.value.lval, 此时的lval是个整型的指示器, 然后PHP会再根据这个指示器在PHP内建的一个资源列表中查询相对应的资源(这部分的内容,我以后会单独开一个篇文章来介绍),目前,你只要知道此时的lval就好像是对应于资源链表的偏移值。

     ZEND_FETCH_RESOURCE(con, type, zval *, default, resource_name, resource_type);
    

    借用这样的机制,PHP就实现了弱类型,因为对于ZE的来说,它所面对的永远都是同一种类型,那就是zval。
    ps:明天team出去building,我想着应该在走之前写点东西给我的blog reader来消磨周末。今天就简单先开个头,下一次,我将进一步介绍PHP的变量,作用域,以及变量的copy on write和change on write机制, 待续....

    45 Comments

    1. php元变量
      php元变量 May 8, 2018

      让脚本语言变量的自带类型前缀, 比如前缀00表示整数,10表示浮点,10表示指针,11表示字符串, 范围不够再续接或者字符实现,牺牲少量容量自带变量类型。最常用的变量都用这种建议结构存储,复杂类型再用_zval_struct,会不会更高效一点?

    2. An outstanding share! I have just forwarded this onto a co-worker who
      has been doing a little research on this.
      And he actually bought me breakfast due to the fact that I stumbled
      upon it for him… lol. So allow me to reword this….
      Thanks for the meal!! But yeah, thanks for spending time
      to discuss this issue here on your web site.

    3. […] [2]laruence. 深入理解PHP原理之变量(Variables inside PHP):http://www.laruence.com/2008/08/22/412.html […]

    4. I get pleasure from, result in I found exactly whuat I was
      having a look for. You have ended myy 4 day longg hunt!
      God Bless you man. Have a nice day. Bye

    5. hanuman chalisa
      hanuman chalisa June 4, 2016

      What’s up mates, its enormous paragraph regarding educationand completely defined, keep it up all the
      time.

    6. 燕玉苗
      燕玉苗 January 29, 2016

      好文,PHP和C都学习了

    7. A motivating discussion is definitely worth comment.
      I think that you ought to write more about this topic, it
      may not be a taboo matter but generally folks don’t discuss
      such issues. To the next! Best wishes!!

    8. descubre más
      descubre más December 21, 2015

      If you desire to increase your knowledge just keep visiting this web page and
      be updated with the latest news posted here.

    9. Dr Uday Tamhankar
      Dr Uday Tamhankar November 6, 2015

      Very rapidly this web site will be famous amid
      all blog users, due to it’s pleasant articles

    10. directory
      directory May 12, 2015

      What i do not realize is if truth be told
      how you’re no longer really a lot more well-favored than you might be
      now. You’re so intelligent. You know thus significantly on the subject
      of this topic, produced me in my view believe it from a lot of numerous angles.
      Its like women and men don’t seem to be fascinated except it
      is one thing to do with Woman gaga! Your own stuffs outstanding.
      All the time care for it up!

    11. เอมมูร่า
      เอมมูร่า March 11, 2015

      Hi, yup this post is really good and I have learned lot of things from it concerning blogging.
      thanks.

    12. Link exchange is nothing else but it is just placing the other person’s
      webpage link on your page at appropriate place and other person will also
      do similar for you.

    13. Hurrah, that’s what I was seeking for, what a stuff!
      present here at this webpage, thanks admin of
      this web page.

    14. […] 晚上自己用例子简单测试下,情况基本属实,特此记录下。 后者效率几乎是前者的3倍。 具体原因是,在PHP中,所有的变量都是用一个结构-zval来保存的,strlen虽然是直接获取其中的len,但是仍然有一次函数调用,而isset是PHP的语法结构,所以更快! 示例: […]

    15. Download Manager
      Download Manager October 17, 2014

      Other reasons forr why AVG won’t install depend on thee
      error messages that you receive aand bewlow are some of thhe
      known reasoons why AVG installation will fail. But the carrier’s home
      page ( can help you find iit in a flash. 10 meanwhile offers the same image downloading features of thee
      Batch Images Downlkader extension ‘ and more.

    16. […] 晚上自己用例子简单测试下,情况基本属实,特此记录下。 后者效率几乎是前者的3倍。 具体原因是,在PHP中,所有的变量都是用一个结构-zval来保存的,strlen虽然是直接获取其中的len,但是仍然有一次函数调用,而isset是PHP的语法结构,所以更快! 示例: […]

    17. […] 在前面的文章中我已经介绍了PHP的变量的内部表示(深入理解PHP原理之变量(Variables inside PHP)),以及PHP中作用域的实现机制(深入理解PHP原理之变量作用域(Scope inside PHP))。这节我们就接着前面的文章,继续介绍PHP中变量分离和引用的概念: […]

    18. […] 在前面的文章中我已经介绍了PHP的变量的内部表示(深入理解PHP原理之变量(Variables inside PHP)),以及PHP中作用域的实现机制(深入理解PHP原理之变量作用域(Scope inside PHP))。这节我们就接着前面的文章,继续介绍PHP中变量分离和引用的概念: […]

    19. […] 了解了,关于变量下面两篇laruence文章有提到,特别是第二篇,个人觉得很好,真心建议坛友们多看看,估计很多人都看过了,挺出名的,再结合php官方那篇垃圾回收机制的文章,效果更好。变量存储http://www.laruence.com/2008/08/22/412.html变量分离引用http://www.laruence.com/2008/09/19/520.html […]

    20. 亚飞正传
      亚飞正传 November 20, 2012

      今天看到这篇,依然学到不少,谢谢。经典blog

    21. 写了个php简单扩展
      写了个php简单扩展 June 14, 2012

      […] 公司有个项目,有需要根据用户编号通过hash来计算用户下载记录所存储的数据库名和表名的算法,数据库用的是MySQL,下载记录的存储一共是100 * 100,也就是有100个库,每个库中有100张表,而具体的存储则是通过用户编号的hash计算出库名表名,然后将该用户的记录存储到相应表中,用户编号是字符串型的。原本这个算法C++部门已经实现了,按理没我什么事了,可是这回记录的读取要我们php这边去数据库直接读取,而为了计算个数据库和表的编号弄个接口似乎麻烦了,于是乎就把C++的该算法的逻辑发过来给我们php来实现。 原本以为很简单的事情,可是弄了之后问题来了,因为该算法中位运算使用的变量是unsigned long int型数据,而我们知道在php中所有的int型其实都是long int型的数据,都是有符号的,没有无符号长整型。关于php变量问题可以参见鸟哥的博文,PHP变量,这是网上别人转载的,鸟哥原文地址为 PHP变量,不过似乎暂时无法访问。 这时候就想到了PHP扩展,因为php扩展其实就是写c程序了,原先的C++的程序稍微改了一下也就可以通过gcc编译顺利运行了,代码如下: […]

    22. 雪候鸟
      雪候鸟 August 23, 2011

      @Forever 不是结构体, 是union 😉

    23. Forever
      Forever August 23, 2011

      照这样说每个变量占用的内存都比较大,因为它是结构体类型啊。结构体占用的内存不是所有类型加起来的嘛

    24. liano
      liano March 23, 2011

      牛人啊,我现在在学php能否给我个联系地址请教你一下啊?
      qq:584418561

    25. 51nosql
      51nosql March 18, 2011

      向大牛学习,关注

    26. […] 如果你在php4和php5中分别执行上述代码,则php4 得到 21php5 得到 22 unset只是将$t指向的zval结构体的refcount – 1,然后清除符号表里的’t',去除$t与zval的关联但是$s仍然是关联zval的。我说不明白,有一篇高手写的文章专门讲这个的,在本坛我贴出来不少次了,你认真看看。http://www.laruence.com/2008/08/22/412.html 关键是这篇http://www.laruence.com/2008/09/19/520.html然后再结合php的GC原理会更深入一些。http://www.php.net/manual/en/features.gc.refcounting-basics.php […]

    27. Anonymous
      Anonymous March 25, 2010

      牛人

    28. yzcj007
      yzcj007 November 26, 2008

      感谢搂主,写得很棒,加油写,等着看呢!

    29. 狂笨地太阳
      狂笨地太阳 September 23, 2008

      太好了,终于理解PHP的变量是怎么个样子了。。PHP变量类型不同,ZVAL的TYPE就不同。

    30. 雪候鸟
      雪候鸟 August 27, 2008

      我指的Zend/zend.h是相当于PHP源码结构树的根目录
      比如,我看的是PHP5.2,那么Zend就是
      PHP5.2-SRC/Zend/

    31. bush
      bush August 27, 2008

      在PHP中,所有的变量都是用一个结构-zval来保存的, 在Zend/zend.h中我们可以看到zval的定义:
      在php应用中 Zend相关的文件没有找到或见过 它属于哪一部分呢

    32. fj
      fj August 26, 2008

      mark

    33. 左手
      左手 August 23, 2008

      太佩服你了,强大的很

    Comments are closed.