风雪之隅
msgbartop
PHP源码分析,PHP源码研究,Zend引擎分析,Web相关技术研究,Web技术分享–左手代码 右手诗
msgbarbottom

24 Aug 08 PHP源码分析之Global关键字

闲来无事,就系统的从PHP的词法分析,语法分析,opcodes生成,执行,整个流程,详细的分析了global关键字的实现。

当你在脚本中写下:

<?php
    
$var = "laruence";
    
function sample(){
        
global $var;
    
}
?>

的时候,你知道PHP是怎么实现在函数作用域找到全局变量的么?

在我前面的文章中(深入理解PHP原理之Opcodes)讲过, PHP的执行,经历了如下几个阶段:

1. Scanning(Lexing) ,将PHP代码转换为语言片段(Tokens)

2. Parsing, 将Tokens转换成简单而有意义的表达式

3. Compilation, 将表达式编译成Opocdes

4. Execution, 顺次执行Opcodes,每次一条,从而实现PHP脚本的功能。

那么,第一阶段,自然就是Scanning了, 在词法分析阶段,我们的

global $var;

会被解析为:

T_GLOBAL  var;

接下来是parsing阶段:

T_GLOBAL var;

yacc经过规则:

statement:
|  T_GLOBAL global_var_list ';'
....

global_var_list:
        global_var_list ',' global_var  { zend_do_fetch_global_variable(&$3, NULL, ZEND_FETCH_GLOBAL_LOCK TSRMLS_CC); }
    |   global_var                      { zend_do_fetch_global_variable(&$1, NULL, ZEND_FETCH_GLOBAL_LOCK TSRMLS_CC); }
;

其中, zend_do_fetch_global_variable是真正生成opcode的函数:

zend_op  *opline;
      ......
      
opline->opcode = ZEND_FETCH_W;     
      
opline->result.op_type = IS_VAR;
      ......
      
opline->op2.u.EA.type = ZEND_FETCH_GLOBAL_LOCK;

而对于ZEND_FETCH_W的op_handler是:

ZEND_VM_HANDLER(83, ZEND_FETCH_W, CONST|TMP|VAR|CV, ANY)
    
{
        
ZEND_VM_DISPATCH_TO_HELPER_EX(zend_fetch_var_address_helper, type, BP_VAR_W);
    
}

我们来看看zend_fetch_var_adress_helper:

.....
        
target_symbol_table = zend_get_target_symbol_table(opline, EX(Ts), type, varname TSRMLS_CC);
/*
        if (!target_symbol_table) {
            ZEND_VM_NEXT_OPCODE();
        }
*/

        
if (zend_hash_find(target_symbol_table, varname->value.str.val, varname->value.str.len+1, (void **) &retval) == FAILUR
E) {
            
switch (type) {
                
case BP_VAR_R:
                
case BP_VAR_UNSET:
                    
zend_error(E_NOTICE,"Undefined variable: %s", Z_STRVAL_P(varname));
                    
/* break missing intentionally */
                
case BP_VAR_IS:
                    
retval = &EG(uninitialized_zval_ptr);
                    
break;
                
case BP_VAR_RW:
                    
zend_error(E_NOTICE,"Undefined variable: %s", Z_STRVAL_P(varname));
                    
/* break missing intentionally */
                
case BP_VAR_W: {
                        
zval *new_zval = &EG(uninitialized_zval);
 
                        
new_zval->refcount++;
                        
zend_hash_update(target_symbol_table, varname->value.str.val, varname->value.str.len+1, &new_zval, siz
eof(zval *), (void **) &retval);
                    
}
                    
break;
                
EMPTY_SWITCH_DEFAULT_CASE()
            
}
        
}

可以看出,核心就是zend_get_targer_symbol_table这个函数了:

static inline HashTable *zend_get_target_symbol_table(zend_op *opline, temp_variable *Ts, int type, zval *variable TSRMLS_DC)
{
    
switch (opline->op2.u.EA.type) {
        
case ZEND_FETCH_LOCAL:
            
return EG(active_symbol_table);
            
break;
        
case ZEND_FETCH_GLOBAL:
        
case ZEND_FETCH_GLOBAL_LOCK:
            
return &EG(symbol_table);
            
break;
        
case ZEND_FETCH_STATIC:
            
if (!EG(active_op_array)->static_variables) {
                
ALLOC_HASHTABLE(EG(active_op_array)->static_variables);
                
zend_hash_init(EG(active_op_array)->static_variables, 2, NULL, ZVAL_PTR_DTOR, 0);
            
}
            
return EG(active_op_array)->static_variables;
            
break;
        
EMPTY_SWITCH_DEFAULT_CASE()
    
}
    
return NULL;
}

恩,问题清楚了,也就是说, 如果你global了一个变量,那么Zend就会去全局symbol_table去寻找,如果找不到,就会在全局symbol_table中分配相应的变量。
通过这样的机制,实现了全局变量。

Related Posts:

TrackBack URI:

Tags: , ,



Reader's Comments

  1. |

    之前我们遇到了一个问题:
    关于PHP class 的顺序的问题,问题如下:
    hello();

    提示找不到 class b .

    这个问题,是什么原因呢?

    回复

  2. |

    哎,居然,把 <? 过滤掉了。

    回复

  3. |

    再发一边。
    <?php
    class a extends b {
    public function hello(){echo “ok1″;}
    };
    class b extends c {
    public function hello(){echo “ok2″;}
    };
    class c {
    public function hello (){echo “ok3″;}
    };

    $a = new a();
    $a->hello();

    回复

  4. |

    这个问题确实奇怪, 一层继承的时候就没有问题。估计是PHP5的一个bug。
    回头我研究研究。

    回复

  5. |

    那篇讲多重继承的bug的文章呢?只在阅读器上看到了….

    回复

  6. |

    恩,因为刚才我发现我的思路可能有点问题,所以又重新验证了一下,这个过程中怕误导别人,就先给隐藏了。 现在好了。

    回复



Leave a Comment

click to change Verify Code(required)click the picture to change