PHP内核探索:全局变量Global
PHP内核    2019-04-25 15:51:05    5    0    0
admin   PHP内核

global语句的作用是定义全局变量,例如如果想在函数内访问全局作用域内的变量则可以通过global声明来定义。 下面从语法解释开始分析。

1. 词法解析

查看 Zend/zend_language_scanner.l文件,搜索 global关键字。我们可以找到如下代码:

1<ST_IN_SCRIPTING>"global" {
2return T_GLOBAL;
3}

2. 语法解析

在词法解析完后,获得了token,此时通过这个token,我们去Zend/zend_language_parser.y文件中查找。找到相关代码如下:

1|   T_GLOBAL global_var_list ';'
2  
3global_var_list:
4    global_var_list ',' global_var  { zend_do_fetch_global_variable(&$3, NULL, ZEND_FETCH_GLOBAL_LOCK TSRMLS_CC); }
5|   global_var                      { zend_do_fetch_global_variable(&$1, NULL, ZEND_FETCH_GLOBAL_LOCK TSRMLS_CC); }
6;

上面代码中的$3是指global_var(如果不清楚yacc的语法,可以查阅yacc入门类的文章。)

从上面的代码可以知道,对于全局变量的声明调用的是zend_do_fetch_global_variable函数,查找此函数的实现在Zend/zend_compile.c文件。

01void zend_do_fetch_global_variable(znode *varname, const znode *static_assignment, int fetch_type TSRMLS_DC)
02{
03        ...//省略
04        opline->opcode = ZEND_FETCH_W;      /* the default mode must be Write, since fetch_simple_variable() is used to define function arguments */
05        opline->result.op_type = IS_VAR;
06        opline->result.u.EA.type = 0;
07        opline->result.u.var = get_temporary_variable(CG(active_op_array));
08        opline->op1 = *varname;
09        SET_UNUSED(opline->op2);
10        opline->op2.u.EA.type = fetch_type;
11        result = opline->result;
12  
13        ... // 省略
14        fetch_simple_variable(&lval, varname, 0 TSRMLS_CC); /* Relies on the fact that the default fetch is BP_VAR_W */
15  
16        zend_do_assign_ref(NULL, &lval, &result TSRMLS_CC);
17        CG(active_op_array)->opcodes[CG(active_op_array)->last-1].result.u.EA.type |= EXT_TYPE_UNUSED;
18}
19/* }}} */

上面的代码确认了opcode为ZEND_FETCH_W外,还执行了zend_do_assign_ref函数。zend_do_assign_ref函数的实现如下:

01void zend_do_assign_ref(znode *result, const znode *lvar, const znode *rvar TSRMLS_DC) /* {{{ */
02{
03        zend_op *opline;
04  
05       ... //省略
06  
07        opline = get_next_op(CG(active_op_array) TSRMLS_CC);
08        opline->opcode = ZEND_ASSIGN_REF;
09       ...//省略
10        if (result) {
11                opline->result.op_type = IS_VAR;
12                opline->result.u.EA.type = 0;
13                opline->result.u.var = get_temporary_variable(CG(active_op_array));
14                *result = opline->result;
15        else {
16                /* SET_UNUSED(opline->result); */
17                opline->result.u.EA.type |= EXT_TYPE_UNUSED;
18        }
19        opline->op1 = *lvar;
20        opline->op2 = *rvar;
21}

从上面的zend_do_fetch_global_variable函数和zend_do_assign_ref函数的实现可以看出, 使用global声明一个全局变量后,其执行了两步操作,ZEND_FETCH_W和ZEND_ASSIGN_REF。

3. 生成并执行中间代码

我们看下ZEND_FETCH_W的最后执行。从代码中我们可以知道:

  • ZEND_FETCH_W = 83
  • op->op1.op_type = 4
  • op->op2.op_type = 0

而计算最后调用的方法在代码中的体现为:

1zend_opcode_handlers[opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]];

计算,最后调用ZEND_FETCH_W_SPEC_CV_HANDLER函数。即

1static int ZEND_FASTCALL  ZEND_FETCH_W_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
2{
3    return zend_fetch_var_address_helper_SPEC_CV(BP_VAR_W, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
4}

在zend_fetch_var_address_helper_SPEC_CV中调用如下代码获取符号表

1target_symbol_table = zend_get_target_symbol_table(opline, EX(Ts), type, varname TSRMLS_CC);

在zend_get_target_symbol_table函数的实现如下:

01static inline HashTable *zend_get_target_symbol_table(const zend_op *opline, const temp_variable *Ts, int type, const zval *variable TSRMLS_DC)
02{
03        switch (opline->op2.u.EA.type) {
04                ... //  省略
05                case ZEND_FETCH_GLOBAL:
06                case ZEND_FETCH_GLOBAL_LOCK:
07                        return &EG(symbol_table);
08                        break;
09               ...  //  省略
10        }
11        return NULL;
12}

在前面语法分析过程中,程序传递的参数是 ZEND_FETCH_GLOBAL_LOCK,于是如上所示。我们取&EG(symbol_table);的值。这也是全局变量的存放位置。

如上就是整个global的解析过程。

上一篇: PHP内核探索:变量类型的转换

下一篇: PHP内核探索:变量的value和type存储

5
登录 后评论.
没有帐号? 现在注册.
0 评论
Table of content