百分百源码网-让建站变得如此简单! 登录 注册 签到领金币!

主页 | 如何升级VIP | TAG标签

当前位置: 主页>网站教程>网页制作> php函数道理
分享文章到:

php函数道理

发布时间:09/01 来源:未知 浏览: 关键词:

前言

  在任何说话中,函数都是最根本的组成单元。关于php的函数,它具是什么特点?函数调取是如何实现的?php函数的机能怎样,有什么使用倡议?本文 将从道理动身停止剖析结合实际的机能测试尝试对这些问题停止答复,在理解实现的同时更好的编写php程序。同时也会对一些常见的php函数停止介绍。

php函数的分类

  在php中,横向划分的话,函数分为两大类: user function(内置函数) 和internal function(内置函数)。前者就是会员在程序中自定义的一些函数和办法,后者则是php本身供给的各类库函数(比方sprintf、 array_push等)。会员也可以通过扩展的办法来编写库函数,这个将在后面介绍。关于user function,又可以细分为function(函数)和method(类办法),本文中将就这三种函数离别停止剖析和测试。

引荐教程:PHP视频教程

php函数的实现

一个php函数终究是怎样施行,这个流程是如何样的呢?

要答复这个问题,我们先来看看php代码的施行所经过的流程。

php61.jpg

  从图中可以看到,php实现了一个典型的动态说话施行历程:拿到一段代码后,经过词法解析、语法解析等阶段后,源程序会被翻译成一个个指令 (opcodes),然后ZEND虚拟机按序施行这些指令完成操纵。Php本身是用c实现的,因此终究调取的也都是c的函数,实际上,我们可以把php看 做是一个c开发的软件。

  通过上面描写不难看出,php中函数的施行也是被翻译成了opcodes来调取,每次函数调取实际上是施行了一条或多条指令。

  关于每一个函数,zend都通过以下的数据构造来描写

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;
        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;

typedef struct _zend_function_state {
    HashTable *function_symbol_table;
    zend_function *function;
    void *reserved[ZEND_MAX_RESERVED_RESOURCES];
} zend_function_state;

  其中type标明了函数的类型:会员函数、内置函数、重载函数。Common中包括函数的根本信息,包罗函数名,参数信息,函数标记(一般函数、静态办法、抽象办法)等内容。别的,关于会员函数,还有一个函数符号表,记载了内部变量等,这个将在后面详述。 Zend保护了一个全局function_table,这是一个大的hahs表。函数调取的时候会第一按照函数名从表中寻到对应的zend_function。当停止函数调取时候,虚拟时机按照type的不一样决议调取办法, 不一样类型的函数,其施行道理是不雷同的 。

内置函数

  内置函数,其本质上就是真正的c函数,每一个内置函数,php在终究编译后都会展开成为一个名叫zif_xxxx的function,比方我们常见 的sprintf,对应到底层就是zif_sprintf。Zend在施行的时候,假如发明是内置函数,则只是简便的做一个转发操纵。

  Zend供给了一系列的api供调取,包罗参数猎取、数组操纵、内存分配等。内置函数的参数猎取,通过zend_parse_parameters 办法来实现,关于数组、字符串等参数,zend实现的是浅拷贝,因此这个效力是很高的。可以这样说,关于php内置函数,其效力和响应c函数几乎雷同,唯 一多了一次转发调取。

  内置函数在php中都是通过so的方式停止动态加载,会员也可以按照需要本人编写响应的so,也就是我们常说的扩展。ZEND供给了一系列的api供扩展使用

会员函数

  和内置函数比拟,会员通过php实现的自定义函数具有完全不一样的施行历程和实现道理。如前文所述,我们知道php代码是被翻译成为了一条条 opcode来施行的,会员函数也不例外,实际中每个函数对应到一组opcode,这组指令被留存在zend_function中。于是,会员函数的调取 终究就是对应到一组opcodes的施行。

部分变量的留存及递归的实现

  我们知道,函数递归是通过堆栈来完成的。在php中,也是利用相似的办法来实现。Zend为每个php函数 分配了一个活动符号表(active_sym_table),记载当前函数中所有部分变量的状态。所有的符号表通过堆栈的情势来保护,每当有函数调取的时 候,分配一个新的符号表并入栈。当调取完毕后当前符号表出栈。由此实现了状态的留存和递归。

  关于栈的保护,zend在这里做了优化。预先分配一个长度为N的静态数组来模拟堆栈,这种通过静态数组来模拟动态数据构造的手法在我们本人的程序中 也经常有使用,这种方式幸免了每次调取带来的内存分配、烧毁。ZEND只是在函数调取完毕时将当前栈顶的符号表数据clean掉即可。

  由于静态数 组长度为N,一旦函数调取层次超越N,程序不会显现栈溢出,这种状况下zend就会停止符号表的分配、烧毁,因此会致使机能下落许多。在zend里面,N 当前取值是32。因此,我们编写php程序的时候,函数调取层次最好不要超越32。当然,假如是web利用,本身可以函数调取层次的深度。

参数的传递

  和内置函数调取zend_parse_params来猎取参数不一样,会员函数中参数的猎取是通过指令来完成的。函数有几个参数就对应几条指令。详细到实现上就是一般的变量赋值。

  通过上面的剖析可以看出,和内置函数比拟,由于是本人保护堆栈表,并且每条指令的施行也是一个c函数,会员函数的机能相对会差许多,后面会有详细的对照剖析。因此,假如一个功效有对应php内置函数实现的尽量不要本人从新写函数去实现。

类办法

  类办法其施行道理和会员函数是雷同的,也是翻译成opcodes按序调取。类的实现,zend用一个数据构造zend_class_entry来实现,里面留存了类相关的一些根本信息。这个entry是在php编译的时候就已经处置完成。

  在zend_function的common中,有一个成员叫做scope,其指向的就是当前办法对应类的zend_class_entry。关于 php中面向对象的实现,这里就不在做更具体的介绍,今后将专门写一篇文章来详述php中面向对象的实现道理。就函数这一块来说,method实现道理和 function完全雷同,理论上其机能也差不多,后面我们将做具体的机能对照。

函数名长度对机能的影响

测试办法

  对名字长度为1、2、4、8、16的函数停止比力,测试比力它们每秒可施行次数,肯定函数名长度对机能的影响

测试结果如下图

php62.jpg

结果剖析

  从图上可以看出,函数名的长度对机能还是会有必然的影响。一个长度为1的函数和长度为16的 空函数调取 ,其机能差了1倍。剖析一下源码不难寻到缘由,如前面论述所说,函数调取的时候zend会先在一个全局的funtion_table中通过函数名查询相关信息,function_table是一个哈希表。必定的,名字越长查询所需要的时间就越多。 因此,在实际编写程序的时候,对屡次调取的函数,名字倡议不要太长

  虽然函数名长度对机能有必然影响,但详细有多大呢?这个问题应当还是需要结合实际状况来思考,假如一个函数本身比力复杂的话,那么对团体的机能影响并不大。

  一个倡议是关于那些会调取许多次,本身功效又比力简便的函数,可以恰当取一些三言两语的名字。

函数个数对机能的影响

测试办法

  在以下三种环境下停止函数调取测试,剖析结果:1.程序仅包括1个函数 2.程序包括100个函数 3.程序包括1000个函数。

  测试这三种状况下每秒所能调取的函数次数

测试结果如下图

php63.jpg

结果剖析

  从测试结果可以看出,这三种状况下机能几乎雷同,函数个数增添时机能下落微不足道,可以忽略。

  从实现道理剖析,几种实现下独一的不同在于函数猎取的部分。如前文所述,所有的函数都放在一个hash表中,在不一样个数下查寻效力都应当还是接近于O(1),所以机能差距不大。

不一样类型函数调取耗损

测试办法

  拔取会员函数、类办法、静态办法、内置函数各一种,函数本身不做任何事情,直接返回,主要测试空函数调取的耗损。测试结果为每秒可施行次数

  测试中为去除其他影响,所有函数名字长度雷同

测试结果如下图

php64.jpg

结果剖析

  通过测试结果可以看到,关于会员本人编写的php函数,不管是哪品种型,其效力是差不多的,均在280w/s摆布。如我们预 期,即便是空调,内置函数其效力也要高许多,到达780w/s,是前者是3倍。可见,内置函数调取的开销还是远低于会员函数。从前面道理剖析可知主要差距 在于会员函数调取时初始化符号表、接收参数等操纵。

内置函数和会员函数机能对照

测试办法

  内置函数和会员函数的机能对照,这里我们拔取几个常用的函数,然后用php实现雷同功效的函数停止一下机能对照。

  测试中,我们拔取字符串、数学、数组中各一个典型停止对照,这几个函数离别是字符串截取(substr)、10进制转2进制(decbin)、求最小值(min)和返回数组中的所以key(array_keys)。

测试结果如下图

php65.jpg

结果剖析

  从测试结果可以看出,如我们预测,内置函数在总体机能上远高于一般会员函数。特别关于触及到字符串类操纵的函数,差距到达了1个数目级。因此,函数使用的一个原则就是假如某功效有响应的内置函数,尽量使用它而不是本人编写php函数。

  关于一些触及到大量字符串操纵的功效,为提高机能,可以思考用扩展来实现。比方常见的富文本过滤等。

  和C函数机能对照

测试办法

  我们拔取字符串操纵和算术运算各3种函数停止比对,php用扩展实现。三种函数是简便的一次算法运算、字符串比力和屡次的算法运算。

  除了本身的两类函数外,还会测试将函数空调开销去除后的机能,一方面比对一下两种函数(c和php内置)本身的机能差别,别的就是侧面印证空调函数的耗损

  测试点为施行10w次操纵的时间耗损

测试结果如下图

php66.jpg

结果剖析

  内置函数和C函数的开销在去除php函数空调取的影响后差距较小,随着函数功效越来越复杂,双方机能趋近于雷同。这个从此前的函数实现剖析中也容易得到论证,究竟内置函数就是C实现的。

  函数功效越复杂,c和php的机能差距越小

  相对c来说,php函数调取的开销大许多,关于简便函数来说机能还是有必然影响。因此php中函数不宜嵌套封装太深。

伪函数及其机能

  在php中,有这样一些函数,它们在使用上是标准的函数用途,但底层实现却和真正函数调取完全不一样,这些函数不属于前文提到的三种function中的任何一类,其本色是一条独自的opcode,这里估且叫做伪函数或者指令函数。

  如上所说,伪函数使用起来和标准的函数并无二致,看起来具有雷同的特点。但是他们终究施行的时候是被zend反映成了一条对应的指令(opcode)来调取,因此其实现更接近于if、for、算术运算等操纵。

php中的伪函数

  1、isset

  2、empty

  3、unset

  4、eval

  通过上面的介绍可以看出,伪函数由于被直接翻译成指令来施行,和一般函数比拟少了一次函数调取所带来的开销,因此机能会更好一些。我们通过如下测试来做一个对照。 Array_key_exists和isset两者都可以推断数组中某个key可否存在,看一下他们的机能

php67.jpg

  从图上可以看出,和array_key_exists比拟,isset机能要高出许多,根本是前者的4倍摆布,而即便是和空函数调取比拟,其机能也要高出1倍摆布。由此也侧面印证再次说明了php函数调取的开销还是比力大的。

以上就是php函数道理的具体内容,更多请关注百分百源码网其它相关文章!

打赏

打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

百分百源码网 建议打赏1~10元,土豪随意,感谢您的阅读!

共有155人阅读,期待你的评论!发表评论
昵称: 网址: 验证码: 点击我更换图片
最新评论

本文标签

广告赞助

能出一分力是一分吧!

订阅获得更多模板

本文标签

广告赞助

订阅获得更多模板