php底层原理之函数

智极漫步
• 阅读 1951
对于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;

从上面介绍的内容,我们可以发现,不管是用户自定义函数还是内部函数,在底层存储时都会存在共有字段typecommon,所以他们以联合体的方式共享内存,可以节省内存空间和快速获取函数的基本信息,并且如果有需要,可以在一些结构间完美的进行强制类型转换。即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内部函数的尽量使用内部函数)

今天我们就聊到这里了,欢迎大家的手动点赞~

点赞
收藏
评论区
推荐文章
莎利亚 莎利亚
4年前
PHP学习笔记之PHP的函数应用
目录一、函数的定义二、自定义函数三、函数的工作原理和结构化编程四、PHP变量的范围五、声明及应用各种形式的PHP函数六、递归函数七、使用自定义函数库一、函数的定义一个被命名的、独立的代码段,它执行特定的任务,并可能给调用它的程序返回一个值。定义中的各部分如下:函数是被命名的:每个函数都
Stella981 Stella981
3年前
PHP使用array_unique对二维数组去重处理
array\_unique函数就是可以处重的,它具备了这个功能了,下面我们一来看一个关于PHP使用array\_unique对二维数组去重处理例子。php5.2.9版本增加了array\_unique对多维数组的支持,在处理多维数组是需要设置sort\_flags参数一维数组的重复项:使用array\_unique函数即可,使用实例如下:
Wesley13 Wesley13
3年前
thinkphp5
在进行项目开发的时候,系统自带的助手函数往往满足不了自己的需求,就需要通过自定义助手函数来实现某个功能,具体做法:!(https://img2018.cnblogs.com/blog/1119605/201903/111960520190325141348203213714126.png)新建:myhelper.php写入:<?
Stella981 Stella981
3年前
PHP strtr 函数
strtr一个一般业务开发很少用到函数;string strtr ( string $str , string $from , string $to )string strtr ( string $str , array $replace_pairs )有两种用法;三个参数:当传
Wesley13 Wesley13
3年前
PHP 常用函数(三)
<?php/年份数字转中文@paraminteger$year年份数字@paramboolean$flag是否显示公元@returnstring/fu
Wesley13 Wesley13
3年前
URL相关函数
http://127.0.0.1/php/example/URL/pg2.php?user\_nameSERENE问号之前的部分是页面正常的URL,问号之后是附加URL上的部分相关函数:arrayparse\_url(stringurl)该函数对URL字符进行解析该数组中包含了解析的结果。stringurlencode(string
Wesley13 Wesley13
3年前
PHP如何快速读取大文件
PHP如何快速读取大文件在PHP中,对于文件的读取时,最快捷的方式莫过于使用一些诸如file、file\_get\_contents之类的函数,简简单单的几行代码就能很漂亮的完成我们所需要的功能。但当所操作的文件是一个比较大的文件时,这些函数可能就显的力不从心,下面将从一个需求入手来说明对于读取大文件时,常用的操作方法。需求需求有一个
Stella981 Stella981
3年前
Phper 学 C 兴趣入门
引子为什么php手册里经常说某个函数是二进制安全的?我们平常使用函数的时候也没发现有什么区别呀,那么二进制安全到底是什么意思呢?Php实验<?phpechostrlen("abc");//3echostrlen("abc\0");//4echostrlen("abc\0
Wesley13 Wesley13
3年前
3_PHP表达式_1_常量
以下为学习孔祥盛主编的《PHP编程基础与实例教程》(第二版)所做的笔记。PHP常量分为自定义常量与预定义常量。1.自定义常量  在使用前必须先定义,PHP的define()函数专门用于定义自定义常量,define()函数的语法格式为:define(name,value\,booleancase\_insensitive\)。  
Stella981 Stella981
3年前
JavaScript学习总结(3)——JavaScript函数(function)
一、函数基本概念  为完成某一功能的程序指令(语句)的集合,称为函数。二、JavaScript函数的分类  1、自定义函数(我们自己编写的函数),如:functionfunName(){}  2、系统函数(JavaScript自带的函数),如alert函数。三、函数的调用方式
Wesley13 Wesley13
3年前
PHP中的NOW()函数
是否有一个PHP函数以与MySQL函数NOW()相同的格式返回日期和时间?我知道如何使用date()做到这一点,但是我问是否有一个仅用于此的函数。例如,返回:2009120100:00:001楼使用此功能:functiongetDatetimeNow(){