摘要:但是到底是如何找到对应的函数的呢今天,我们来一起寻找答案函数分类首先,我们先回顾一下的函数分类,函数包含用户自定义函数内部函数匿名函数等多种类型。用户自定义函数和内部函数编译完成后会将函数名注册到全局函数列表中。
对于PHPer而言,我们通常会把常用的功能封装成一个函数来进行复用,以提升开发效率。但是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互换
函数注册聊完了用户自定义函数和内部函数的数据结构存储,我们再来看下全局函数列表
全局函数列表,可以理解成函数注册表,其内部实现是一个哈希表。用户自定义函数和内部函数编译完成后会将函数名注册到全局函数列表中。也就是此时会判断是否全局函数列表中存在同名函数
那么用户自定义函数和内部函数在存储到全局函数列表时有什么不同呢?
用户自定义函数:
我们写的php函数在编译阶段会经过词法分析->语法分析->生成中间代码opcode->执行中间代码的过程,执行中间代码时,会将函数名注册到全局函数列表
内部函数:
php启动时,会加载所有扩展模块,并为扩展模块中每一个函数创建一个zend_internal_function结构,并将这个函数名注册到全局函数列表
接下来,我们再来看下调用函数时,是如何执行的呢?
函数调用时,首先会根据函数名去全局函数列表内查找是否存在该函数,如果不存在,则会直接报出“Call to undefined function xxx()"的错误信息;如果存在,则获取该函数指针对应的函数结构中的type字段,判断其函数类型,如果函数类型是自定义函数,则调用zend_execute来执行函数的zend_op_array内容,而如果函数类型是内部函数,则直接调用zend_internal_function的handle指针指向的扩展模块的C函数
总结到这里,大家应该可以找到开头我们提出的问题的答案了。其实就是通过函数注册到全局函数列表,然后函数调用时,再从全局函数列表中查找对应函数进行执行来实现的;只不过,对于用户自定义函数和内部函数而言,其实现方式不同,当然这也就意味着执行效率的不同(当然php内部函数执行效率更高了,因为它没有运行时的编译阶段,相当于直接执行c语言,所以能用php内部函数的尽量使用内部函数)
今天我们就聊到这里了,欢迎大家的手动点赞~
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/31237.html
摘要:对于来说,变量有全局变量和局部变量之分那么,他们都是存储到一个哈希表内了么其实不是的,变量存储也有作用域的概念。 上次跟大家讲了垃圾回收机制后,有些小伙伴对底层原理比较感兴趣,私信问我了一些关于变量的相关知识,既然大家对变量比较感兴趣,那么这次我们来系统的讲一下变量的底层原理 变量结构 首先,我们还是先摆上我们的zval结构体,即php所有变量都会以zval结构体的形式实现 struc...
摘要:数组是最常用的数据类型,同时容易上手也得益于其强大的数组,但是数组在中是如何实现的呢首先,我们还是先了解下相关的数据结构,为下面的内容打好基础哈希表哈希表,顾名思义,即将不同的关键字映射到不同单元的一种数据结构。 数组是PHPer最常用的数据类型,同时php容易上手也得益于其强大的数组,但是数组在php中是如何实现的呢? 首先,我们还是先了解下相关的数据结构,为下面的内容打好基础 哈希...
摘要:但是对于结构体中的和字段我们一直都没有详细介绍过,而这两个字段其实是和变量之间赋值的原理有着密切的关系的。 上周我们从底层的角度介绍了php变量从生成->常量赋值->销毁的完整生命周期(不了解的同学可以翻看一下前面的文章php底层原理之变量(一)),但是我们留了一个思考,不知道大家有答案了没,变量之间的赋值在底层又是如何实现的呢? 变量之间赋值 php变量的zval结构,我们已经介绍了...
摘要:总结垃圾回收机制以的引用计数机制为基础以前只有该机制同时使用根缓冲区机制,当发现有存在循环引用的时,就会把其投入到根缓冲区,当根缓冲区达到配置文件中的指定数量后,就会进行垃圾回收,以此解决循环引用导致的内存泄漏问题开始引入该机制 php垃圾回收机制,对于PHPer来说是一个不陌生但是又不是很熟悉的内容。那么php是怎么实现对不需要的内存进行回收的呢? php变量的内部存储结构 首先还是...
阅读 2203·2021-11-22 11:56
阅读 2649·2021-10-08 10:05
阅读 7781·2021-09-22 15:53
阅读 1914·2021-09-22 15:29
阅读 2236·2021-09-08 09:35
阅读 3357·2021-09-07 10:12
阅读 1381·2019-08-30 13:11
阅读 1971·2019-08-28 17:54