码迷,mamicode.com
首页 > Web开发 > 详细

php函数的实现

时间:2018-05-28 19:20:04      阅读:216      评论:0      收藏:0      [点我收藏+]

标签:ast   定义   内核   image   code   bsp   技术分享   标准   var   

1.函数     汇编中函数对应的是一组独立的汇编指令,然后通过call指令实现函数的调用。PHP编译的opcode数组,与汇编指令对应。

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

 

2.函数的存储结构

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;

    zend_op_array op_array; //函数实际编译为普通的zend_op_array
    zend_internal_function internal_function;
};

 

      这是一个union,PHP中函数除了用户自定义函数还有一种:内部函数,内部函数是通过扩展或者内核提供的C函数,比如time、array系列等等。内部函数主要用到internal_function,而用户自定义函数编译完就是一个普通的opcode数组,用的是op_array(注意:op_array、internal_function是嵌入的两个结构,而不是一个单独的指针)

     PHP脚本的生命周期中有一个非常重要的值executor_globals(EG宏实际就是对executor_globals的操作),类型是struct _zend_executor_globals,它记录着PHP生命周期中所有的数据,如果你写过PHP扩展一定用到过。

     EG(function_table)是一个哈希表,记录的就是PHP中所有的函数。PHP在编译阶段将用户自定义的函数编译为独立的opcodes,保存在EG(function_table)中。调用时重新分配新的zend_execute_data(相当于运行栈),然后执行函数的opcodes,调用完再还原到旧的zend_execute_data,继续执行。

 

3.函数的参数  

        函数参数在实现上与函数内的局部变量实际是一样的,编译的时候会有一个单独的 编号 ,参数名称保存在zend_op_array.vars中,编号首先是从参数开始的,所以按照参数顺序其编号依次为0、1、2...(转化为相对内存偏移量就是96、112、128...),函数调用时首先会在调用位置将参数的value复制到各参数各自的位置。

      参数还有一个存储参数信息的zend_arg_info结构

typedef struct _zend_arg_info {
    zend_string *name; //参数名
    zend_string *class_name;
    zend_uchar type_hint; //显式声明的参数类型,比如(array $param_1)
    zend_uchar pass_by_reference; //是否引用传参,参数前加&的这个值就是1
    zend_bool allow_null; //是否允许为NULL,注意:这个值并不是用来表示参数是否为必传的
    zend_bool is_variadic; //是否为可变参数,即...用法,与golang的用法相同,5.6以上新增的一个用法:function my_func($a, ...$b){...}
} zend_arg_info;

 

4.函数的编译     

        PHP代码的编译过程,主要是PHP->AST->Opcodes的转化,函数其实就是将一组PHP代码编译为单独的opcodes,函数的调用就是不同opcodes间的切换,所以函数的编译过程与普通PHP代码基本一致。

     过程:

           a.保存当前正在编译的zend_op_array,新分配一个结构,因为每个函数、include的文件都对应独立的一个zend_op_array,通过CG(active_op_array)记录当前编译所属zend_op_array,所以开始编译函数时就需要将这个值保存下来,等到函数编译完成再还原回去,在当前zend_op_array(不是新生成的函数那个)生成一条 ZEND_DECLARE_FUNCTION 的opcode,也就是函数声明操作。

           b.编译参数列表,完整的参数会有三个组成:参数类型(可选)、参数名、默认值(可选),这三部分分别保存在参数节点的三个child节点中,编译参数的过程有两个关键操作:为每个参数编号、每个参数生成一条opcode

           c.编译函数内部语法,这个跟普通PHP代码编译过程无异

           d.将IS_CONST、IS_VAR、IS_TMP_VAR类型的操作数、返回值转化为内存偏移量,设置各指令的处理handler

     最终生成两个zend_op_array:

 

                                                技术分享图片

 

        PHP在逐行编译时发现一个function则生成一条ZEND_DECLARE_FUNCTION的opcode,然后调到函数中编译函数,编译完再跳回去继续下面的编译,这里多次提到ZEND_DECLARE_FUNCTION这个opcode是因为在函数编译结束后还有一个重要操作:zend_do_early_binding(),核心工作就是 将function、class加到CG(function_table)、CG(class_table)中 ,加入成功了就直接把 ZEND_DECLARE_FUNCTION 这条opcode干掉了,加入失败的话则保留,这个相当于 有一部分opcode在『编译时』提前执行了 ,这也是为什么PHP中可以先调用函数再声明函数的原因。

5.匿名函数     匿名函数(Anonymous functions),也叫闭包函数(closures,允许临时创建一个没有指定名称的函数。最经常用作回调函数(callback)参数的值。当然,也有其它应用的情况。

6.内部函数     内部函数指的是由内核、扩展提供的C语言编写的function,这类函数不需要经历opcode的编译过程,所以效率上要高于PHP用户自定义的函数,调用时与普通的C程序没有差异。

      函数的结构zend_function为union,其中其中internal_function就是内部函数

        

//zend_complie.h
typedef struct _zend_internal_function {
    /* Common elements */
    zend_uchar type;
    zend_uchar arg_flags[3]; /* bitset of arg_info.pass_by_reference */
    uint32_t fn_flags;
    zend_string* function_name;
    zend_class_entry *scope;
    zend_function *prototype;
    uint32_t num_args;
    uint32_t required_num_args;
    zend_internal_arg_info *arg_info;
    /* END of common elements */

    void (*handler)(INTERNAL_FUNCTION_PARAMETERS); //函数指针,展开:void (*handler)(zend_execute_data *execute_data, zval *return_value)
    struct _zend_module_entry *module;
    void *reserved[ZEND_MAX_RESERVED_RESOURCES];
} zend_internal_function;

        zend_internal_function头部是一个与zend_op_array完全相同的common结构。内部函数与用户自定义函数冲突,用户无法在PHP代码中覆盖内部函数,执行PHP脚本时会提示error错误。

        内部函数的定义,我们只需要创建一个普通的C函数,然后创建一个zend_internal_function结构添加到 EG(function_table) (也可能是CG(function_table),取决于在哪一阶段注册)中即可使用,内部函数 通常 情况下是在php_module_startup阶段注册的,通常是按照标准的扩展定义内部函数。

 

php函数的实现

标签:ast   定义   内核   image   code   bsp   技术分享   标准   var   

原文地址:https://www.cnblogs.com/hellohell/p/9101727.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!