· 作者: Laruence( )
· 本文地址: http://www.laruence.com/2008/08/24/377.html
· 转载请注明出处
闲来无事,就系统的从PHP的词法分析,语法分析,opcodes生成,执行,整个流程,详细的分析了global关键字的实现。
当你在脚本中写下:
1. <?php
2. $var = "laruence";
3. functionsample(){
4. global$var;
5. }
6. ?>
的时候,你知道PHP是怎么实现在函数作用域找到全局变量的么?
在我前面的文章中(深入理解PHP原理之Opcodes)讲过, PHP的执行,经历了如下几个阶段:
1. 1. Scanning(Lexing) ,将PHP代码转换为语言片段(Tokens)
2.
3. 2. Parsing, 将Tokens转换成简单而有意义的表达式
4.
5. 3. Compilation, 将表达式编译成Opocdes
6.
7. 4. Execution, 顺次执行Opcodes,每次一条,从而实现PHP脚本的功能。
那么,第一阶段,自然就是Scanning了,在词法分析阶段,我们的
1. global$var;
会被解析为:
1. T_GLOBAL var;
接下来是parsing阶段:
1. T_GLOBAL var;
yacc经过规则:
1. statement:
2. | T_GLOBAL global_var_list ';'
3. ....
4.
5. global_var_list:
6. global_var_list',' global_var {zend_do_fetch_global_variable(&$3, NULL, ZEND_FETCH_GLOBAL_LOCK TSRMLS_CC);}
7. | global_var {zend_do_fetch_global_variable(&$1, NULL, ZEND_FETCH_GLOBAL_LOCK TSRMLS_CC);}
8. ;
其中, zend_do_fetch_global_variable是真正生成opcode的函数:
1. zend_op *opline;
2. ......
3. opline->opcode = ZEND_FETCH_W;
4. opline->result.op_type = IS_VAR;
5. ......
6. opline->op2.u.EA.type = ZEND_FETCH_GLOBAL_LOCK;
而对于ZEND_FETCH_W的op_handler是:
1. ZEND_VM_HANDLER(83, ZEND_FETCH_W, CONST|TMP|VAR|CV, ANY)
2. {
3. ZEND_VM_DISPATCH_TO_HELPER_EX(zend_fetch_var_address_helper, type, BP_VAR_W);
4. }
我们来看看zend_fetch_var_adress_helper:
1. .....
2. target_symbol_table= zend_get_target_symbol_table(opline, EX(Ts), type, varname TSRMLS_CC);
3. /*
4. if(!target_symbol_table) {
5. ZEND_VM_NEXT_OPCODE();
6. }
7. */
8. if (zend_hash_find(target_symbol_table, varname->value.str.val, varname->value.str.len+1, (void **) &retval) == FAILUR
9. E) {
10. switch (type) {
11. case BP_VAR_R:
12. case BP_VAR_UNSET:
13. zend_error(E_NOTICE,"Undefined variable: %s",Z_STRVAL_P(varname));
14. /* break missing intentionally */
15. case BP_VAR_IS:
16. retval= &EG(uninitialized_zval_ptr);
17. break;
18. case BP_VAR_RW:
19. zend_error(E_NOTICE,"Undefined variable: %s",Z_STRVAL_P(varname));
20. /* break missing intentionally */
21. case BP_VAR_W: {
22. zval *new_zval = &EG(uninitialized_zval);
23.
24. new_zval->refcount++;
25. zend_hash_update(target_symbol_table, varname->value.str.val, varname->value.str.len+1, &new_zval, siz
26. eof(zval *), (void **) &retval);
27. }
28. break;
29. EMPTY_SWITCH_DEFAULT_CASE()
30. }
31. }
可以看出,核心就是zend_get_targer_symbol_table这个函数了:
1. staticinline HashTable *zend_get_target_symbol_table(zend_op *opline, temp_variable *Ts, int type, zval *variable TSRMLS_DC)
2. {
3. switch (opline->op2.u.EA.type) {
4. case ZEND_FETCH_LOCAL:
5. returnEG(active_symbol_table);
6. break;
7. case ZEND_FETCH_GLOBAL:
8. case ZEND_FETCH_GLOBAL_LOCK:
9. return &EG(symbol_table);
10. break;
11. case ZEND_FETCH_STATIC:
12. if (!EG(active_op_array)->static_variables) {
13. ALLOC_HASHTABLE(EG(active_op_array)->static_variables);
14. zend_hash_init(EG(active_op_array)->static_variables, 2, NULL, ZVAL_PTR_DTOR, 0);
15. }
16. returnEG(active_op_array)->static_variables;
17. break;
18. EMPTY_SWITCH_DEFAULT_CASE()
19. }
20. return NULL;
21. }
恩,问题清楚了,也就是说,如果你global了一个变量,那么Zend就会去全局symbol_table去寻找,如果找不到,就会在全局symbol_table中分配相应的变量。
通过这样的机制,实现了全局变量。