PHP深刻明PHP内核(十一)函数-函数的内部结构

原稿链接:http://www.orlion.ga/330/

php的函数包括用户定义之函数、内部函数(print_r
count…)、匿名函数、变量函数($func = ‘print_r’; $func(array(‘a’,’b’));)

PHP内核源码中将函数分为以下项目

#define ZEND_INTERNAL_FUNCTION              1#define ZEND_USER_FUNCTION                  2  
#define ZEND_OVERLOADED_FUNCTION            3#define ZEND_EVAL_CODE                      4#define ZEND_OVERLOADED_FUNCTION_TEMPORARY  5

无异于、用户函数(ZEND_USER_FUNCTION)

  函数不肯定显式的来返回值,在PHP的落实着虽没有显式的回来,PHP内核也会见帮助我们回来NULL。

  ZEND于履行过程中,会拿运行时信息存储于_zend_execute_data中:

PHP 1

struct _zend_execute_data {    //...省略部分代码    zend_function_state function_state;
    zend_function *fbc; /* Function Being Called */
    //...省略部分代码};

PHP 2

  在程序初始化的长河中,function_state也会见开展初始化,function_state由少数个组成部分组成:

typedef struct _zend_function_state {
    zend_function *function;    void **arguments;
} zend_function_state;

  *arguments是一个针对函数参数的指针,而函数体本事存储于*function中,*function是一个zend_function结构体,它说到底存储了用户从定义函数的漫天信息,具体组织如下:

PHP 3

typedef union _zend_function {
    zend_uchar type;    /* MUST be the first element of this struct! */
 
    struct {
        zend_uchar type;  /* never used */
        char *function_name;    //函数名称
        zend_class_entry *scope; //函数所在的类作用域
        zend_uint fn_flags;     //函数类型,如用户自定义则为 #define ZEND_USER_FUNCTION 2  
        union _zend_function *prototype; //函数原型
        zend_uint num_args;     //参数数目
        zend_uint required_num_args; //需要的参数数目
        zend_arg_info *arg_info;  //参数信息指针        zend_bool pass_rest_by_reference;
        unsigned char return_reference;  //返回值    } common;
 
    zend_op_array op_array;   //函数中的操作‰
    zend_internal_function internal_function;  
} zend_function;

PHP 4

  zend_function的结构体中的op_array存储了该函数惨遭之有所操作,当函数被调用时,ZEND就会见用这op_array中之opline一条条顺序执行,并拿最后的结果返回。函数的概念及履行是分开的,一个函数可以看成一个单独的周转单元有。

仲、内部函数(ZEND_INTERNAL_FUNCTION)

  ZEND_INTERNAL_FUNCTION函数是由扩张或者Zend/PHP内核提供的,用c/c++编写,可以一直实施的函数,以下为其中函数的构造

PHP 5

typedef struct _zend_internal_function {    /* Common elements */
    zend_uchar type;    char * function_name;
    zend_class_entry *scope;
    zend_uint fn_flags;
    union _zend_function *prototype;
    zend_uint num_args;
    zend_uint required_num_args;
    zend_arg_info *arg_info;
    zend_bool pass_rest_by_reference;
    unsigned char return_reference;    /* END of common elements */
 
    void (*handler)(INTERNAL_FUNCTION_PARAMETERS);    struct _zend_module_entry *module;
} zend_internal_function;

PHP 6

  以模块初始化的当儿,ZE会遍历每个载入的扩张模块,然后以模块中function_entry中指明的各级一个函数(module->functions),创建一个zend_internal_function结构,并将其type设置为ZEND_INTERNAL_FUNCTION,将此布局填入全局的函数表(HashTable结构);函数设置与注册过程见Zend/zene_API.c文件被的zend_register_function函数,这个函数除了处理函数页为处理类的计,包括那些魔术点子。

  内部函数的布局以及用户从定义函数结构基本相仿,有一对见仁见智:

  •   调用方法,handler字段,如果是ZEND_INTERNAL_FUNCTION,那么ZEND就会见调用zend_execute_internal,通过zend_internal_function.handler来执行此函数。而用户从定义函数需要扭转中间代码,然后经过中间代码映射到对立就拿办法调用。

  • 停放函数在构造中多矣一个module字段,表示属于哪个模块。不同之扩大模块不同

  • type字段,在用户从定义函数中,type字段几乎无用,而坐函数中的type字段作为几种中函数的分别。

老三、变量函数

  如果一个变量名后边有圆括号,php将搜跟变量的价值同名的函数,并且尝试推行。

  变量函数$func

$func = 'print_r';
$func('i am print_r function.');

  编译后中间代码

PHP 7

function name:  (null)
number of ops:  9compiled vars:  !0 = $func
line     # *  op                           fetch          ext  return operands------------------------------------------------------------------------------
-
-   2     0  >   EXT_STMT         1      ASSIGN                                                   !0, 
'print_r'
   3     2      EXT_STMT         3      INIT_FCALL_BY_NAME                                       !0
         4      EXT_FCALL_BEGIN         5      SEND_VAL                                                 
'i+am+print_r+function.'
         6      DO_FCALL_BY_NAME                              1
         7      EXT_FCALL_END         8    > RETURN                                  1

PHP 8

  内部函数

print_r('i am print_r function.');

  编译后中间代码

PHP 9

function name:  (null)
number of ops:  6compiled vars:  none
line     # *  op                           fetch          ext  return  operands-------------------------------------------------------------------------------
-
-   2     0  >   EXT_STMT         1      EXT_FCALL_BEGIN         2      SEND_VAL                                                 
'i+am+print_r+function.'
         3      DO_FCALL                                      1          'print_r'
         4      EXT_FCALL_END         5    > RETURN                                                   1

PHP 10

  对比发现,二者在调用中间代码上设有有的有别于,变量函数是DO_FCALL_BY_NAME,而其间函数是DO_FCALL。这在语法解析时就曾控制了,见Zend/zend_complie.c文件的zend_do_end_function_call函数中有代码:

PHP 11

if (!is_method && !is_dynamic_fcall && function_name->op_type==IS_CONST) {
        opline->opcode = ZEND_DO_FCALL;
        opline->op1 = *function_name;
        ZVAL_LONG(&opline->op2.u.constant, 
zend_hash_func(Z_STRVAL(function_name->u.constant), Z_STRLEN(function_name-
>u.constant) + 1));
    } else {
        opline->opcode = ZEND_DO_FCALL_BY_NAME;
        SET_UNUSED(opline->op1);
    }

PHP 12

  如果不是道,并且不是动态调用,并且函数称吧字符串变量,则该变动的中间代码为ZEND_DO_FCALL。其他情况虽然为ZEND_DO_FCALL_BY_NAME。另外将变量函数作为回调函数,其处理过程在Zend/zend_complie.c文件的zend_do_pass_param函数中,最终见面体现在中间代码执行过程遭到之ZEND_SEND_VAL_SPEC_CONST_HADNLER等函数中。

 

季、匿名函数

  匿名函数是如出一辙近似不需要指定表示可,而而足以被调用的函数或子例程,匿名函数可以一本万利之当作参数传递给其他函数。

 

相关文章