闲来无事,就系统的从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了, 在词法分析阶段,我们的
会被解析为:
接下来是parsing阶段:
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 ( 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 )) ; 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 )) ; 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: global , PHP , PHP应用
Leave a Comment
之前我们遇到了一个问题:
关于PHP class 的顺序的问题,问题如下:
hello();
提示找不到 class b .
这个问题,是什么原因呢?
回复
哎,居然,把 <? 过滤掉了。
回复
再发一边。
<?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();
回复
这个问题确实奇怪, 一层继承的时候就没有问题。估计是PHP5的一个bug。
回头我研究研究。
回复
那篇讲多重继承的bug的文章呢?只在阅读器上看到了….
回复
恩,因为刚才我发现我的思路可能有点问题,所以又重新验证了一下,这个过程中怕误导别人,就先给隐藏了。 现在好了。
回复