标签 - php7内核解析

php7内核解析    2019-09-16 14:41:46    34    0    0

3.2 函数实现

函数,通俗的讲就是一组操作的集合,给予特定的输入将对应特定的输出。

3.2.1 用户自定义函数的实现

用户自定义函数是指我们在PHP脚本通过function定义的函数:

function my_func(){
    ...
}

汇编中函数对应的是一组独立的汇编指令,然后通过call指令实现函数的调用。前面已经说过PHP编译的结果是opcode数组,与汇编指令对应。PHP用户自定义函数的实现就是将函数编译为独立的opcode数组,调用时分配独立的执行栈依次执行opcode,所以自定义函数对于zend而言并没有什么特别之处,只是将opcode进行了打包封装。PHP脚本中函数之外的指令,整个可以认为是一个函数(或者理解为main函数更直观)。

/* function main(){ */

$a = 123;
echo $a;

/* } */

3.2.1.1 函数的存储结构

下面具体看下PHP中函数的结构:

typedef union  _zend_function        zend_function;

//zend_compile.h
union _zend_function {
    zend_uchar type;    /* MUST be the first element of this struct! */

    struct {
        zend_uchar type;  /* never used */
        zend_uchar arg_flags[3]; /* bitset of arg_info.pass_by_reference */
        uint32_t fn_flags;
        zend_string *function_name;
        zend_class_entry *scope; //成员方法所属类,面向对象实现中用到
        union _zend_function *prototype;
        uint32_t num_args; //参数数量
        uint32_t required_num_args; //必传参数数量
        zend_arg_info *arg_info; //参数信息
    } common;

  
php7内核解析    2019-09-16 14:40:12    27    0    0

3.1.1 词法解析、语法解析

这一节我们分析下PHP的解析阶段,即 PHP代码->抽象语法树(AST) 的过程。

PHP使用re2c、bison完成这个阶段的工作:

  • re2c: 词法分析器,将输入分割为一个个有意义的词块,称为token
  • bison: 语法分析器,确定词法分析器分割出的token是如何彼此关联的

例如:

$a = 2 + 3;

词法分析器将上面的语句分解为这些token:$a、=、2、+、3,接着语法分析器确定了2+3是一个表达式,而这个表达式被赋值给了a,我们可以这样定义词法解析规则:

/*!re2c
    LABEL   [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*
    LNUM    [0-9]+

    //规则
    "$"{LABEL} {return T_VAR;}
    {LNUM} {return T_NUM;}
*/

然后定义语法解析规则:

//token定义
%token T_VAR
%token T_NUM

//语法规则
statement:
    T_VAR '=' T_NUM '+' T_NUM {ret = str2int($3) + str2int($5);printf("%d",ret);}
;

上面的语法规则只能识别两个数值相加,假如我们希望支持更复杂的运算,比如:

$a = 3 + 4 - 6;

则可以配置递归规则:

//语法规则
statement:
    T_VAR '=' expr {}
;
expr:
    T_NUM {...}
    |expr '?' T_NUM {}
;

这样将支持若干表达式,用语法分析树表示:

接下来我们看下PHP具体的解析过程,PHP编译阶段流程:

zend_compile_process

其中 zendparse() 就是词法、语法解析过程,这个函数实际就是bison中提供的语法解析函数 yyparse() :

#define yyparse         zendparse

yyparse() 不断调用 yylex() 得到token,然后根据token匹配语法规则:

#define yylex           zendlex

//zend_compile.c
int zendlex(zend_parser_stack_elem *elem)
{
    zval zv;
    int