php的函数包括用户定义的函数、内部函数(print_r count...)、匿名函数、变量函数($func = 'print_r'; $func(array('a','b'));)

PHP内核源码中将函数分为以下类型

  1. #define ZEND_INTERNAL_FUNCTION 1
  2. #define ZEND_USER_FUNCTION 2
  3. #define ZEND_OVERLOADED_FUNCTION 3
  4. #define ZEND_EVAL_CODE 4
  5. #define ZEND_OVERLOADED_FUNCTION_TEMPORARY 5

一、用户函数(ZEND_USER_FUNCTION)

  函数不一定显式的有返回值,在PHP的实现中即使没有显式的返回,PHP内核也会帮我们返回NULL。

  ZEND在执行过程中,会将运行时信息存储于_zend_execute_data中:

  1. struct _zend_execute_data {
  2. //...省略部分代码
  3. zend_function_state function_state;
  4. zend_function *fbc; /* Function Being Called */
  5. //...省略部分代码
  6. };

  在程序初始化的过程中,function_state也会进行初始化,function_state由两个部分组成:

  1. typedef struct _zend_function_state {
  2. zend_function *function;
  3. void **arguments;
  4. } zend_function_state;

  *arguments是一个指向函数参数的指针,而函数体本事存储于*function中,*function是一个zend_function结构体,它最终存储了用户自定义函数的一切信息,具体结构如下:

  1. typedef union _zend_function {
  2. zend_uchar type; /* MUST be the first element of this struct! */
  3.  
  4. struct {
  5. zend_uchar type; /* never used */
  6. char *function_name; //函数名称
  7. zend_class_entry *scope; //函数所在的类作用域
  8. zend_uint fn_flags; //函数类型,如用户自定义则为 #define
  9. ZEND_USER_FUNCTION
  10. union _zend_function *prototype; //函数原型
  11. zend_uint num_args; //参数数目
  12. zend_uint required_num_args; //需要的参数数目
  13. zend_arg_info *arg_info; //参数信息指针
  14. zend_bool pass_rest_by_reference;
  15. unsigned char return_reference; //返回值
  16. } common;
  17.  
  18. zend_op_array op_array; //函数中的操作
  19. ‰
  20. zend_internal_function internal_function;
  21. } zend_function;

  zend_function的结构体中的op_array存储了该函数中的所有操作,当函数被调用时,ZEND就会将这个op_array中的opline一条条顺序执行,并将最后的结果返回。函数的定义和执行是分开的,一个函数可以作为一个独立的运行单元存在。

二、内部函数(ZEND_INTERNAL_FUNCTION)

  ZEND_INTERNAL_FUNCTION函数是由扩展或者Zend/PHP内核提供的,用c/c++编写,可以直接执行的函数,以下为内部函数的结构

  1. typedef struct _zend_internal_function {
  2. /* Common elements */
  3. zend_uchar type;
  4. char * function_name;
  5. zend_class_entry *scope;
  6. zend_uint fn_flags;
  7. union _zend_function *prototype;
  8. zend_uint num_args;
  9. zend_uint required_num_args;
  10. zend_arg_info *arg_info;
  11. zend_bool pass_rest_by_reference;
  12. unsigned char return_reference;
  13. /* END of common elements */
  14.  
  15. void (*handler)(INTERNAL_FUNCTION_PARAMETERS);
  16. struct _zend_module_entry *module;
  17. } zend_internal_function;

  在模块初始化的时候,ZE会遍历每个载入的扩展模块,然后将模块中function_entry中指明的每一个函数(module->functions),创建一个zend_internal_function结构,并将其type设置为ZEND_INTERNAL_FUNCTION,将这个结构填入全局的函数表(HashTable结构);函数设置及注册过程见Zend/zene_API.c文件中的zend_register_function函数,这个函数除了处理函数页也处理类的方法,包括那些魔术方法。

  内部函数的结构与用户自定义函数结构基本类似,有一些不同:

  •   调用方法,handler字段,如果是ZEND_INTERNAL_FUNCTION,那么ZEND就会调用zend_execute_internal,通过zend_internal_function.handler来执行这个函数。而用户自定义函数需要生成中间代码,然后通过中间代码映射到相对就把方法调用。
  • 内置函数在结构中多了一个module字段,表示属于哪个模块。不同的扩展模块不同
  • type字段,在用户自定义函数中,type字段几乎无用,而内置函数中的type字段作为几种内部函数的区分。

三、变量函数

  如果一个变量名后边有圆括号,php将寻找与变量的值同名的函数,并且尝试执行。

  变量函数$func

  1. $func = 'print_r';
  2. $func('i am print_r function.');

  编译后中间代码

  1. function name: (null)
  2. number of ops:
  3. compiled vars: ! = $func
  4. line # * op fetch ext return operands
  5. ------------------------------------------------------------------------------
  6. -
  7. -
  8. > EXT_STMT
  9. ASSIGN !,
  10. 'print_r'
  11. EXT_STMT
  12. INIT_FCALL_BY_NAME !
  13. EXT_FCALL_BEGIN
  14. SEND_VAL
  15. 'i+am+print_r+function.'
  16. DO_FCALL_BY_NAME
  17. EXT_FCALL_END
  18. > RETURN                           1

  内部函数

  1. print_r('i am print_r function.');

  编译后中间代码

  1. function name: (null)
  2. number of ops:
  3. compiled vars: none
  4. line # * op fetch ext return operands
  5. -------------------------------------------------------------------------------
  6. -
  7. -
  8. > EXT_STMT
  9. EXT_FCALL_BEGIN
  10. SEND_VAL
  11. 'i+am+print_r+function.'
  12. DO_FCALL
  13. 'print_r'
  14. EXT_FCALL_END
  15. > RETURN

  对比发现,二者在调用中间代码上存在一些区别,变量函数是DO_FCALL_BY_NAME,而内部函数是DO_FCALL。这在语法解析时就已经决定了,见Zend/zend_complie.c文件的zend_do_end_function_call函数中部分代码:

  1. if (!is_method && !is_dynamic_fcall && function_name->op_type==IS_CONST) {
  2. opline->opcode = ZEND_DO_FCALL;
  3. opline->op1 = *function_name;
  4. ZVAL_LONG(&opline->op2.u.constant,
  5. zend_hash_func(Z_STRVAL(function_name->u.constant), Z_STRLEN(function_name-
  6. >u.constant) + ));
  7. } else {
  8. opline->opcode = ZEND_DO_FCALL_BY_NAME;
  9. SET_UNUSED(opline->op1);
  10. }

  如果不是方法,并且不是动态调用,并且函数名为字符串变量,则其生成的中间代码为ZEND_DO_FCALL。其他情况则为ZEND_DO_FCALL_BY_NAME。另外将变量函数作为回调函数,其处理过程在Zend/zend_complie.c文件的zend_do_pass_param函数中,最终会体现在中间代码执行过程中的ZEND_SEND_VAL_SPEC_CONST_HADNLER等函数中。

四、匿名函数

  匿名函数是一类不需要指定表示符,而又可以被调用的函数或子例程,匿名函数可以方便的作为参数传递给其他函数。

  

深入理解PHP内核(五)函数的内部结构的更多相关文章

  1. 深入理解PHP内核(十一)函数-函数的内部结构

    原文链接:http://www.orlion.ga/330/ php的函数包括用户定义的函数.内部函数(print_r count…).匿名函数.变量函数($func = 'print_r'; $fu ...

  2. 深入理解PHP内核(六)函数的定义、传参及返回值

    一.函数的定义 用户函数的定义从function 关键字开始,如下 function foo($var) { echo $var; } 1.词法分析 在Zend/zend_language_scann ...

  3. 深入理解PHP内核(五)变量及数据类型-变量的结构和类型

    原文链接:http://www.orlion.ga/238/ 编程语言的类型可以分为强类型和弱类型两种,PHP是弱类型语言,但是C语言是强类型语言.在官网PHP实现内部,所有变量使用同一种数据结构(z ...

  4. 深入理解php内核 编写扩展 I:介绍PHP和Zend

    内容: 编写扩展I -  PHP和Zend起步 原文:http://devzone.zend.com/public/view/tag/Extension Part I: Introduction to ...

  5. 深入理解PHP内核(九)变量及数据类型-静态变量

    原文链接:http://www.orlion.ga/251/ 通常静态变量是静态分配的,他们的生命周期和程序的生命周期一样长,只有在程序退出后才结束生命周期,这和局部变量相反,有的语言中全局变量也是静 ...

  6. 深入理解PHP内核(二)概览-PHP生命周期与Zend引擎

    本文参考自<深入理解PHP内核>,地址:https://github.com/reeze/tipi 本文链接:http://www.orlion.ml/232/ 1.SAPI接口 SAPI ...

  7. Linux内核d_path函数应用的经验总结

    问题背景 一个内核模块中,需要通过d_path接口获取文件的路径,然后与目标文件白名单做匹配. 在生产环境中,获取的文件是存在的,但是与文件白名单中的文件总是匹配失败. 问题定位: 通过打印d_pat ...

  8. 读书笔记之Linux系统编程与深入理解Linux内核

    前言 本人再看深入理解Linux内核的时候发现比较难懂,看了Linux系统编程一说后,觉得Linux系统编程还是简单易懂些,并且两本书都是讲Linux比较底层的东西,只不过侧重点不同,本文就以Linu ...

  9. 深入理解php内核

    目录 第一部分 基本原理 第一章 准备工作和背景知识 第一节 环境搭建 第二节 源码布局及阅读方法 第三节 常用代码 第四节 小结 第二章 用户代码的执行 第一节 PHP生命周期 第二节 从SAPI开 ...

随机推荐

  1. <Oracle Database>数据库启动与关闭

    启动和关闭Oracle数据库 要启动和关闭数据库,必须要以具有Oracle 管理员权限的用户登陆,通常也就是以具有SYSDBA权限的用户登陆.一般我们常用INTERNAL用户来启动和关闭数据库(INT ...

  2. appframework build目录各文件之包含内容

    { "build/css/af.ui.css": [ "css/main.css", "css/appframework.css", &qu ...

  3. H264与RTP

    http://blog.163.com/laorenyuhai126@126/blog/static/1935077920111218152989/

  4. Drupal资源

    以下是一些Drupal的常用资源. www.drupal.org:Drupal官网,拥有最全 www.acquia.com:Drupal奠基人Dries主导的专业网站,有著名的Aquia平台,功能类似 ...

  5. eclipse执行上一次结果

    eclipse执行上一次结果,解决方法:Project-clean

  6. codeforces 484E

    题意:给定n<=105的数组h,有m<=105的询问,每个询问为l,r,w求[l,r]区间内连续w个的最小高度最大是多少.. 思路:首先把h数组从大到小排序,然后用建立一个可持久化的下标线 ...

  7. window下安装wamp环境

    Wamp就是Windos Apache Mysql PHP集成安装环境,即在window下的apache.php和mysql的服务器软件.其中php环境配置是至关重要的一部分,本文就针对php在本地的 ...

  8. C++混合编程之idlcpp教程Python篇(9)

    上一篇在这 C++混合编程之idlcpp教程Python篇(8) 第一篇在这 C++混合编程之idlcpp教程(一) 与前面的工程相比,工程PythonTutorial7中除了四个文件PythonTu ...

  9. STC12C5A60S2笔记1(管脚定义)

    STC12C5A60S2管脚定义 管脚1:标准IO口P1.0.ADC0 模数转换通道0.CLKOUT2 波特率发生器的时钟输出 管脚2:标准IO口P1.1.ADC1 模数转换通道1 管脚3:标准IO口 ...

  10. 实战-Fluxion与wifi热点伪造、钓鱼、中间人攻击、wifi破解

    原作者:PG     整理:玄魂工作室-荣杰 目录: 0x00-Fluxion是什么 0x01-Fluxion工作原理 0x02-Kali上安装fluxion 0x03-Fluxion工具使用说明+实 ...