字体:  

深入理解PHP原理之变量作用域

laruence 发表于: 2008-8-26 18:05 来源: PHPChina 开源社区门户

有问题,在这里回帖,和我在blog回复都可以, 我俩边都会看的,;)

· 作者:laruence(http://www.laruence.com/)
· 本文地址: http://www.laruence.com/2008/08/26/463.html
· 转载请注明出处                           
  
   我前面的文章(深入理解PHP原理之变量(Variables inside PHP))介绍了PHP变量的内部表示,但是,问题是,这些内部表示是如何和用户脚本中的变量联系起来的呢?也就是说,如果我在脚本中写下:
<?php
   $var = "laruence";
   
echo  $var;
?>

   ZE是如何把我的变量var和内部结构zval联系起来的呢?
   深入理解PHP原理之变量中讲过,PHP内部都是使用zval来表示变量的,但是对于上面的脚本,我们的变量是有名字的, var。而zval中并没有相应的字段来体现变量名。
    如果你想到了PHP内部一定有一个机制,来实现变量名到zval的映射。那么你真的是很聪明,;)
在PHP中,所有的变量都会存储在一个数组中(确切的说是hash table), 并且,PHP也是通过不同的数组来实现变量的作用域的。
    当你创建一个变量的时候,PHP会为这个变量分配一个zval,填入相应的变量值,然后将这个变量的名字,和指向这个zval的指针填入一个数组中。然后,当你获取这个变量的时候,PHP会通过查找这个数组,获得对应的zval。
   查看_zend_executor_globals结构(这个结构在PHP的执行器保存一些执行相关的上下文信息)
struct
_zend_executor_globals
{

....
HashTable *active_symbol_table;/*活动符号表*/
HashTable
symbol_table;     /*全局符号表*/
HashTable
included_files;   

jmp_buf *bailout;
int error_reporting;
.....
}

  其中,全局符号表,保存了在顶层作用域(就是不在任何函数,对象内)的变量。每当调用一个函数(对象的方法)的时候,就会为这个函数创建一个活动符号表,所有在这个函数内定义的变量,都会保存在这个活动符号表中。
  对,这就是PHP的变量作用域的实现方式! 举个列子:
<?php
    $var = "I am in the global symbol table";
   
function  sample($para){
        $var = "I am in the active symbol table";
        
echo $var;
     
}
     sample($var);
     
echo  $var;
  
?>

  在函数sample外面的变量$var,它会被填入全局符号表中,与他对应的有一个zval指针,这个zval保存了一个字符串”I am in the global symbol table”.
  函数内的$var, 它会被填入属于函数sample的活动符号表中,一样的,与他对应的zval中,保存着字符串”I am in the active symbol table“.

  比较特殊的,就是函数sample的参数$para了,这个$para是保存在sample的活动符号表的,但是与他对应的zval指针,会指向一个保存一份全局变量$var的copy的zval(严格来讲不是copy,是引用,这个涉及到变量的copy on write机制,我会在以后介绍)。
  我们都知道PHP对于简单变量是传值调用的,但是,我要告诉你的是,PHP并不是简单的通过复制一个zval来实现传值的,呵呵,留个悬念,等我下回分解.

[ 本帖最后由 laruence 于 2008-8-26 23:05 编辑 ]

最新回复

521vicky20 at 2008-8-26 18:24:34
如果贴到这里来的话,相信不仅两边,好多边,都会看到的.
laruence at 2008-8-26 18:35:17
代码会乱,,, 好吧,
blankyao at 2008-8-26 22:11:59
laruence,趁我给你寄加精的这会你去修改下样式吧
laruence at 2008-8-26 22:39:49
我是想,只是,,在编辑模式下看不出来有什么问题
这个和我的wp还真是不兼容啊。
只喝可乐的猫 at 2008-8-26 23:03:34
排版
laruence at 2008-8-26 23:12:16
恩,简单修改了下,看起来好多了!
ylcz at 2008-8-27 09:06:45
讲得太好了,不要停啊
laruence at 2008-8-27 10:02:37
有更新, 加入了对类和global关键字的介绍.
CrossMaya at 2008-8-27 10:16:40
牛逼 真是牛逼
leric at 2008-8-27 23:45:49
typedef struct _zval_struct {
    zvalue_value value;
    zend_uint refcount;
    zend_uchar type;
    zend_uchar is_ref;
  } zval;

弄不清楚这里的is_ref怎么用,如果是个引用的话,这里的value就不用保存变量的实际值了,那么这里的value应该是保存个指针之类的东西吧
laruence at 2008-8-28 10:01:47
is_ref的意思是:
$a = 2;
$b = &$a;

那此时a,b都指向同一个zval, 这个zval的is_ref=1
leric at 2008-8-28 10:20:42
哦,是我糊涂了
zval是变量,$a,$b是符号啊,不过zval已经有个refcount引用计数了,如果是这个目的的话refcount就可以替代is_ref了。
在符号表的HashTable里的symbol不知道是什么类型的呢,那个里面应该记录着这个变量是传值还是引用吧。
laruence at 2008-8-28 11:21:57
symbol_table是一个hashTable ,
所以,是key->value
而value是 ** zval
所以, symbol_table本身不记录什么其他信息
leric at 2008-8-28 11:53:19
哦,差不多明白了,那样的话is_ref就是判断是Copy on Write还是直接Write的标志了
那么在执行$a = $b这样的语句的时候就必须要把zval复制一份了吧
laruence at 2008-8-28 11:58:10
恩,基本上差不多了.

但是$a = $b,不会产生separate动作

$a = $b;
$a = 2;  这样会产生.
leric at 2008-8-28 12:35:22
那样在is_ref == 1, refcount >2 的时候会有麻烦的啊
在refcount>=2时创建引用也应该会产生复制吧
laruence at 2008-8-28 13:24:02
恩,还有个separation anxiety(不好翻译):

$a = 2;
$b = &$a

$c = $a;

这个时候,就会separated