global语句的作用是定义全局变量,例如如果想在函数内访问全局作用域内的变量则可以通过global声明来定义。

下面从语法解释开始分析。

1. 词法解析

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

<ST_IN_SCRIPTING>"global" {
return T_GLOBAL;
}

2. 语法解析

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

|   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); }
;

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

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

void zend_do_fetch_global_variable(znode *varname, const znode *static_assignment, int fetch_type TSRMLS_DC) 
{
        ...//省略
        opline->opcode = ZEND_FETCH_W;      /* the default mode must be Write, since fetch_simple_variable() is used to define function arguments */
        opline->result.op_type = IS_VAR;
        opline->result.u.EA.type = 0;
        opline->result.u.var = get_temporary_variable(CG(active_op_array));
        opline->op1 = *varname;
        SET_UNUSED(opline->op2);
        opline->op2.u.EA.type = fetch_type;
        result = opline->result;
 
        ... // 省略
        fetch_simple_variable(&lval, varname, 0 TSRMLS_CC); /* Relies on the fact that the default fetch is BP_VAR_W */
 
        zend_do_assign_ref(NULL, &lval, &result TSRMLS_CC);
        CG(active_op_array)->opcodes[CG(active_op_array)->last-1].result.u.EA.type |= EXT_TYPE_UNUSED;
}
/* }}} */

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

void zend_do_assign_ref(znode *result, const znode *lvar, const znode *rvar TSRMLS_DC) /* {{{ */
{
        zend_op *opline;
 
       ... //省略
 
        opline = get_next_op(CG(active_op_array) TSRMLS_CC);
        opline->opcode = ZEND_ASSIGN_REF;
       ...//省略
        if (result) {
                opline->result.op_type = IS_VAR;
                opline->result.u.EA.type = 0;
                opline->result.u.var = get_temporary_variable(CG(active_op_array));
                *result = opline->result;
        } else {
                /* SET_UNUSED(opline->result); */
                opline->result.u.EA.type |= EXT_TYPE_UNUSED;
        }
        opline->op1 = *lvar;
        opline->op2 = *rvar;
}

从上面的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

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

zend_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函数。即

static int ZEND_FASTCALL  ZEND_FETCH_W_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
return zend_fetch_var_address_helper_SPEC_CV(BP_VAR_W, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
}

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

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

在zend_get_target_symbol_table函数的实现如下:

static inline HashTable *zend_get_target_symbol_table(const zend_op *opline, const temp_variable *Ts, int type, const zval *variable TSRMLS_DC)
{
        switch (opline->op2.u.EA.type) {
                ... //  省略
                case ZEND_FETCH_GLOBAL:
                case ZEND_FETCH_GLOBAL_LOCK:
                        return &EG(symbol_table);
                        break;
               ...  //  省略
        }
        return NULL;
}

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

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

漫谈php全局变量Global的更多相关文章

  1. php中全局变量global和超全局变量$GLOBALS

    php中全局变量global和超全局变量$GLOBALS 1.global Global的作用是定义全局变量,但是这个全局变量不是应用于整个网站,而是应用于当前页面,包括include或require ...

  2. PHP中的全局变量global和$GLOBALS的区别

    1.global Global的作用是定义全局变量,但是这个全局变量不是应用于整个网站,而是应用于当前页面,包括include或require的所有文件. 但是在函数体内定义的global变量,函数体 ...

  3. python 局部变量和全局变量 global

    当你在函数定义内声明变量的时候,它们与函数外具有相同名称的其他变量没有任何关系,即变量名称对于函数来说是 局部 的.这称为变量的 作用域 .所有变量的作用域是它们被定义的块,从它们的名称被定义的那点开 ...

  4. CodeIgniter 定义“全局变量-global variable”,可以在所有controller,model和view中使用

    本文抄自http://www.cnblogs.com/webu/archive/2012/11/20/2779999.html 第一次正儿八经用CodeIgniter框架做项目,结果不会定义全局变量, ...

  5. Python局部变量和全局变量global

    当你在函数定义声明变量的时候,它们与函数外具有相同名称的其它变量没有任何关系,即变量名称对于函数来说是 局部  的.这称为变量的 作用域 .所有变量的作用域是它们被定义的块,从它们的名称被定义的那点开 ...

  6. 关于PHP中的全局变量global和$GLOBALS的不同区分

    1.global Global的作用是定义全局变量,但是这个全局变量不是应用于整个网站,而是应用于当前页面,包括include或require的所有文件. 但是在函数体内定义的global变量,函数体 ...

  7. PHP中全局变量global和$GLOBALS[]的区别分析

    $GLOBALS['var']是外部的全局变量本身,global $var是外部$var的同名引用或者指针     一.举例比较 例一: 复制代码 代码如下: <?php $var1 = 1; ...

  8. php 中全局变量global 的使用

    简介 即使开发一个新的大型PHP程序,你也不可避免的要使用到全局数据,因为有些数据是需要用到你的代码的不同部分的.一些常见的全局数据有:程序设定类.数 据库连接类.用户资料等等.有很多方法能够使这些数 ...

  9. 【PHP】- 全局变量global和$GLOBALS的区别

    1.global global关键字的作用是定义全局变量,但是这个全局变量不是应用于整个网站,而是应用于当前页面,包括include或require的所有文件. 但是在函数体内定义的global变量, ...

随机推荐

  1. Build类

    在开发中 我们有时候会需要获取当前手机的系统版本来进行判断,或者需要获取一些当前手机的硬件信息. android.os.Build类中.包括了这样的一些信息.我们可以直接调用 而不需要添加任何的权限和 ...

  2. JSONP(处理跨域问题)

    Ajax直接请求普通文件存在跨域无权限访问的问题 凡是拥有"src"这个属性的标签都拥有跨域的能力,比如<script>.<img>.<iframe& ...

  3. C# 之 将string数组转换到int数组并获取最大最小值

    1.string 数组转换到 int 数组 " }; int[] output = Array.ConvertAll<string, int>(input, delegate(s ...

  4. Javascript与Ajax

    不使用jquery来处理ajax请求该怎么做? 首先要明确html中的某些数据需要从服务端获得,也就是客户端向服务端请求(request)数据,服务端就响应(response)这个请求,把客户端要的数 ...

  5. 通过 Session 操纵对象

    Session 接口是 Hibernate 向应用程序提供的操纵数据库的最主要的接口, 它提供了基本的保存, 更新, 删除和加载 Java 对象的方法. Session 具有一个缓存, 位于缓存中的对 ...

  6. AngularJs学习经验汇集

    >>关于ng-include 有时候你会发现你用这个指令想要加载某个模板总是加载不出来,url明明是对的,页面还是一片空白,这里有一个细节要注意以下: <div ng-include ...

  7. poj 1185 炮兵阵地 状态压缩dp

    思路:定义一个三维数组dp[x][i][j]其中x为now和pre两种状态,now表示当前两行最优解,pre表示出了本行外,前两行的最优解.那么状态转移方程为 dp[now][j][k]=max(dp ...

  8. Android 给应用定制皮肤

    Android 给应用定制皮肤 导读:皮肤也就是相关的资源文件单独放置在某个工程中,一种皮肤一个工程文件.一个工程包括N多的资源文件,多个工程间资源的关系是,文件名,资源ID等完全一样. 在实现程序功 ...

  9. discuze回放提示“抱歉,您的请求来路不正确或表单验证串不符,无法提交”

    不知从哪里看到文章,但是实用: 背景:discuze就单纯的录制一个注册脚本,日志中没有报错,在报告中就提示"抱歉,您的请求来路不正确或表单验证串不符,无法提交"",以下 ...

  10. hostname

    http://www.linuxidc.com/Linux/2014-11/109238.htm