【PHP内核学习】global关键字的解析过程分析

本文github地址:

https://github.com/wusuopubupt/phpLib/blob/master/global%E5%85%B3%E9%94%AE%E5%AD%97%E7%9A%84%E8%A7%A3%E6%9E%90%E8%BF%87%E7%A8%8B%E5%88%86%E6%9E%90

|=-----------------------------------------------------------------------=|

|=--------------------=[ global关键字的解析过程分析 ]=-------------------=|

|=-----------------------------------------------------------------------=|

|=--------------------------=[  by d4shman  ]=---------------------------=|

|=-----------------------------------------------------------------------=|

|=-------------------------=[  May 8, 2014  ]=---------------------------=|

|=-----------------------------------------------------------------------=|

[目录]

0x01 词法分析 

0X02 语法分析

0X03 解释执行

0X04 参考文献

0x01 词法分析

[email protected]# vi /php-dev/php-5.4.8/Zend/zend_language_scanner.l

找到global:

    <ST_IN_SCRIPTING>"global" {

        return T_GLOBAl;

    }

发现返回一个token T_GLOBAL

0X02 语法分析

通过token T_GLOBAL来到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_val,可以看到,对于全局变量,语法分析器调用的是Zend引擎

的zend_do_fetch_globa_variable函数。此函数的声明在Zend/zend_compile.c

0X03 解释执行

    在Zend/zend_compile.c中找到zend_do_fetch_global_variable函数定义:

    void zend_do_fetch_global_variable(znode *varname, const znode *static_assignment, int fetch_type TSRMLS_DC) 

    {

        zend_op *opline;

        znode lval;

        znode result;

/*如果变量类型是常量且不是字符串,则将其转化成字符串类型*/

        if (varname->op_type == IS_CONST) {  

            if (Z_TYPE(varname->u.constant) != IS_STRING) {

                convert_to_string(&varname->u.constant);

            }

        }

    

        opline = get_next_op(CG(active_op_array) TSRMLS_CC); /* CG: compile_global */

        opline->opcode = ZEND_FETCH_W;      /* 默认的模式必须是Write */

        opline->result_type = IS_VAR;

        opline->result.var = get_temporary_variable(CG(active_op_array));

        SET_NODE(opline->op1, varname);

        if (opline->op1_type == IS_CONST) {

            CALCULATE_LITERAL_HASH(opline->op1.constant);

        }

        SET_UNUSED(opline->op2);

        opline->extended_value = fetch_type;

        GET_NODE(&result, opline->result);

    

        if (varname->op_type == IS_CONST) {

            zval_copy_ctor(&varname->u.constant);

        }

/* Relies on the fact that the default fetch is BP_VAR_W */

        fetch_simple_variable(&lval, varname, 0 TSRMLS_CC); 

    

        zend_do_assign_ref(NULL, &lval, &result TSRMLS_CC);

        CG(active_op_array)->opcodes[CG(active_op_array)->last-1].result_type |= EXT_TYPE_UNUSED;

    }

    上面的代码确认了opcode为ZEND_FETCH_W外,还执行了zend_do_assign_ref函数。zend_do_assign_ref函数中

有这么一个关键语句:

    opline->opcode = ZEND_ASSIGN_REF;

    由此可知,语法分析过程中,实际执行了2个opcode: ZEND_FETCH_W和ZEND_ASSIGN_REF,在zend_vm_opcodes.h

中发现,它们对应的opcode分别是83和39。而计算最后调用的方法是(定义在zend_execute.c:):

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

    计算后(///////////我没搞清楚是怎么计算出的//////////),得到调用的函数是:

    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(int fetch_type TSRMLS_DC)

    {

        switch (fetch_type) {

            case ZEND_FETCH_LOCAL:  

                if (!EG(active_symbol_table)) {

                    zend_rebuild_symbol_table(TSRMLS_C);

                }

                return EG(active_symbol_table);

                break;

            case ZEND_FETCH_GLOBAL:

            case ZEND_FETCH_GLOBAL_LOCK:

                return &EG(symbol_table); /*返回global 变量符号表的地址*/

                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;

    }

通过代码可以看到,当传递过来的fetch_type是ZEND_FETCH_GLOBAL(_LOCK)时,函数使用EG(excutor_global)宏

返回了global变量的符号表地址。

    以上就是global变量解析执行的整个过程。

0X04 参考文献

《深入理解PHP内核》

    

【PHP内核学习】global关键字的解析过程分析

时间: 2024-08-25 22:34:48

【PHP内核学习】global关键字的解析过程分析的相关文章

【PHP内核学习】变量和数据类型

|=-----------------------------------------------------------------------=| |=---------------------=[ PHP内核中的变量和数据类型]=--------------------=| |=-----------------------------------------------------------------------=| |=--------------------------=[ by

【PHP内核学习】深入理解FastCGI

本文github地址:https://github.com/wusuopubupt/phpLib/blob/master/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3FastCGI |=-----------------------------------------------------------------------=| |=------------------------=[ 深入理解FastCGI ]=--------------------------

Linux内核很吊之 module_init解析 (下)

<h1 style="margin:0px;padding:0px;color:#555555;font-family:'microsoft yahei';line-height:35px;white-space:normal;background-color:#FFFFFF;"> <div style="text-align:center;"> <span style="font-size:24px;">Li

Linux网络编程&amp;内核学习

c语言: 基础篇 1.<写给大家看的C语言书(第2版)> 原书名: Absolute Beginner's Guide to C (2nd Edition) 原出版社: Sams 作者: (美)Greg Perry    [作译者介绍] 译者: 谢晓钢 刘艳娟 丛书名: 图灵程序设计丛书 C/C++系列 出版社:人民邮电出版社 ISBN:9787115216359上架时间:2009-12-10出版日期:2010 年1月开本:16开页码:308 说明:这本是入门最好的,最简单,最好懂 2.<

PHP变量入门教程(3)global 关键字

global关键字 首先,一个使用 global 的例子: 使用 global <?php $a = 1; $b = 2; function Sum() { global $a, $b; $b = $a + $b; } Sum(); echo $b; ?> 以上脚本的输出将是 "3".在函数中申明了全局变量 $a 和 $b,任何变量的所有引用变量都会指向到全局变量.对于一个函数能够申明的全局变量的最大个数,PHP 没有限制. 在全局范围内访问变量的第二个办法,是用特殊的 P

PHP 闭包获取外部变量和global关键字声明变量的区别

最近在学习workerman的时候比较频繁的接触到回调函数,使用中经常会因为worker的使用方式不同,会用这两种不同的方式去调用外部的worker变量,这里就整理一下PHP闭包获取外部变量和global关键字声明变量的区别. 闭包 闭包是一个常见的概念,我们通常可以将其与回调函数配合使用,可以使代码更加简洁易读. 闭包可以通过拷贝的方式让函数使用父作用域中的变量.如: $global = 'hello'; $bbb = function()use($global){ echo $global.

Python实用技巧:global关键字的用法详解

这篇文章主要介绍了python global关键字的用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下: 想要更好的了解global关键字,首先要熟悉python的全局变量与局部变量的概念.global关键字的作用是可以申明一个局部变量为全局变量,下面看一下实例 一.变量作用域的说明 1.局部变量 1 def a(): 2 ## 菊部变量 - ,- 3 local = 1 4 print(local) 5 ## 全局无法使用,只有自己可用

Linux内核学习总结

李泽源 原创作品 转载请注明出处 <Linux内核分析>MOOC课程:http://mooc.study.163.com/course/USTC-1000029000 [Linux内核学习总结] 幸福来得很突然,这门课就快结束了…… 是时候,总结下这段时间的坚持了,也给同样对Linux内核有兴趣的你一个指南. 在这门课的学习过程中,按照老师的要求,每次课后都写一篇博文,这是一个很好的学习方式.每当写这些文章的时候,总是要多看几遍视频,再查查相关的资料,才能勉强凑成一个完整的文档:同时也把自己学

Linux内核学习总结(final)

Linux内核学习总结 符钰婧 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 这八周以来,我从拼不出来"Linux"这个词到知道了很多专有名词,也能大概了解Linux的工作机制,这一系列的进步都是一周周积累下来的.现在回过头来看,有种阳光总在风雨后的感觉,虽然这个比喻好像不太恰当. 闲话少说,接下来就进入这次的正题. 一.首先是对Linux操作系统的理解 1.操作系