对于PHPer而言,咱们一般会把经常使用的功能封装成一个函数来进行复用,以提高开发效率。可是php究竟是如何找到对应的函数的呢?今天,咱们来一块儿寻找答案~php
首先,咱们先回顾一下php的函数分类,php函数包含用户自定义函数、内部函数、匿名函数等多种类型。而对于用户自定义函数和内部函数,他们分别存在对应本身的数据结构,可是Zend引擎为了适配两种函数类型,因此定义了一种新的数据结构:zend_function
联合体数据结构
咱们仍是先看下zend_function
联合体,了解下为何针对用户自定义函数和内部函数要作适配函数
typedef union _zend_function {
zend_uchar type; //函数类型,用来标记是用户自定义函数仍是内部函数
struct {
zend_uchar type; /* never used */
char *function_name; //函数名称
zend_class_entry *scope; //函数所在的类做用域,用来标记做为成员方法时所属的类
zend_uint fn_flags; // 标记其做为类的成员方法时的访问类型,是public、protected仍是private
union _zend_function *prototype; //函数原型,标记内部函数或者用户自定义函数所属的zend_function
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;
struct _zend_op_array {
/* 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 */
zend_bool done_pass_two;
....// 其它字段
}
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;
复制代码
从上面介绍的内容,咱们能够发现,无论是用户自定义函数仍是内部函数,在底层存储时都会存在共有字段type
和common
,因此他们以联合体的方式共享内存,能够节省内存空间和快速获取函数的基本信息,而且若是有须要,能够在一些结构间完美的进行强制类型转换。即zend_function
能够与zend_op_array
互换,zend_function
能够与zend_internal_function
互换ui
聊完了用户自定义函数和内部函数的数据结构存储,咱们再来看下全局函数列表spa
全局函数列表,能够理解成函数注册表,其内部实现是一个哈希表。用户自定义函数和内部函数编译完成后会将函数名注册到全局函数列表中。也就是此时会判断是否全局函数列表中存在同名函数prototype
那么用户自定义函数和内部函数在存储到全局函数列表时有什么不一样呢?指针
用户自定义函数: 咱们写的php函数在编译阶段会通过词法分析->语法分析->生成中间代码opcode->执行中间代码的过程,执行中间代码时,会将函数名注册到全局函数列表rest
内部函数: php启动时,会加载全部扩展模块,并为扩展模块中每个函数建立一个zend_internal_function结构,并将这个函数名注册到全局函数列表code
接下来,咱们再来看下调用函数时,是如何执行的呢?内存
函数调用时,首先会根据函数名去全局函数列表内查找是否存在该函数,若是不存在,则会直接报出“Call to undefined function xxx()"的错误信息;若是存在,则获取该函数指针对应的函数结构中的type字段,判断其函数类型,若是函数类型是自定义函数,则调用zend_execute来执行函数的zend_op_array内容,而若是函数类型是内部函数,则直接调用zend_internal_function的handle指针指向的扩展模块的C函数
到这里,你们应该能够找到开头咱们提出的问题的答案了。其实就是经过函数注册到全局函数列表,而后函数调用时,再从全局函数列表中查找对应函数进行执行来实现的;只不过,对于用户自定义函数和内部函数而言,其实现方式不一样,固然这也就意味着执行效率的不一样(固然php内部函数执行效率更高了,由于它没有运行时的编译阶段,至关于直接执行c语言,因此能用php内部函数的尽可能使用内部函数)
今天咱们就聊到这里了,欢迎你们的手动点赞~