下面具体看下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;
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是嵌入的两个结构,而不是一个单独的指针),除了这两个上面还有一个type
跟common
,这俩是做什么用的呢?
经过比较发现zend_op_array
与zend_internal_function
结构的起始位置都有common
中的几个成员,如果你对C的内存比较了解应该会马上想到它们的用法,实际common
可以看作是op_array
、internal_function
的header,不管是什么哪种函数都可以通过zend_function.common.xx
快速访问到zend_function.zend_op_array.xx
及zend_function.zend_internal_function.xx
,下面几个,type
同理,可以直接通过zend_function.type
取到zend_function.op_array.type
及zend_function.internal_function.type
。
函数是在编译阶段确定的,那么它们存在哪呢?
在PHP脚本的生命周期中有一个非常重要的值executor_globals
(非ZTS下),类型是struct _zend_executor_globals
,它记录着PHP生命周期中所有的数据,如果你写过PHP扩展一定用到过EG
这个宏,这个宏实际就是对executor_globals
的操作:define EG(v) (executor_globals.v)
EG(function_table)
是一个哈希表,记录的就是PHP中所有的函数。
PHP在编译阶段将用户自定义的函数编译为独立的opcodes,保存在EG(function_table)
中,调用时重新分配新的zend_execute_data(相当于运行栈),然后执行函数的opcodes,调用完再还原到旧的zend_execute_data
,继续执行,关于zend引擎execute阶段后面会详细分析。