您好,欢迎访问一九零五行业门户网

深入懂得PHP内核(五)函数的内部结构

深入理解php内核(五)函数的内部结构
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中:
struct _zend_execute_data { //...省略部分代码 zend_function_state function_state; zend_function *fbc; /* function being called */ //...省略部分代码};
在程序初始化的过程中,function_state也会进行初始化,function_state由两个部分组成:
typedef struct _zend_function_state { zend_function *function; void **arguments;} zend_function_state;
*arguments是一个指向函数参数的指针,而函数体本事存储于*function中,*function是一个zend_function结构体,它最终存储了用户自定义函数的一切信息,具体结构如下:
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;
zend_function的结构体中的op_array存储了该函数中的所有操作,当函数被调用时,zend就会将这个op_array中的opline一条条顺序执行,并将最后的结果返回。函数的定义和执行是分开的,一个函数可以作为一个独立的运行单元存在。
二、内部函数(zend_internal_function)
zend_internal_function函数是由扩展或者zend/php内核提供的,用c/c++编写,可以直接执行的函数,以下为内部函数的结构
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;
在模块初始化的时候,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.');
编译后中间代码
function name: (null)number of ops: 9compiled vars: !0 = $funcline # * 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
内部函数
print_r('i am print_r function.');
编译后中间代码
function name: (null)number of ops: 6compiled vars: noneline # * 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
对比发现,二者在调用中间代码上存在一些区别,变量函数是do_fcall_by_name,而内部函数是do_fcall。这在语法解析时就已经决定了,见zend/zend_complie.c文件的zend_do_end_function_call函数中部分代码:
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); }
如果不是方法,并且不是动态调用,并且函数名为字符串变量,则其生成的中间代码为zend_do_fcall。其他情况则为zend_do_fcall_by_name。另外将变量函数作为回调函数,其处理过程在zend/zend_complie.c文件的zend_do_pass_param函数中,最终会体现在中间代码执行过程中的zend_send_val_spec_const_hadnler等函数中。
四、匿名函数
匿名函数是一类不需要指定表示符,而又可以被调用的函数或子例程,匿名函数可以方便的作为参数传递给其他函数。
其它类似信息

推荐信息