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

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

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

一、用户函数(ZEND_USER_FUNCTION)

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

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

struct _zend_execute_data {
//...省略部分代码
zend_function_state function_state;
zend_function *fbc; /* Function Being Called */
//...省略部分代码
};

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

typedef struct _zend_function_state {
zend_function *function;
void **arguments;
} zend_function_state;

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

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; //函数类型,如用户自定义则为 #define
ZEND_USER_FUNCTION
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;

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

二、内部函数(ZEND_INTERNAL_FUNCTION)

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

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;

  在模块初始化的时候,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

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

  编译后中间代码

function name:  (null)
number of ops:
compiled vars: ! = $func
line # * op fetch ext return operands
------------------------------------------------------------------------------
-
-
> EXT_STMT
ASSIGN !,
'print_r'
EXT_STMT
INIT_FCALL_BY_NAME !
EXT_FCALL_BEGIN
SEND_VAL
'i+am+print_r+function.'
DO_FCALL_BY_NAME
EXT_FCALL_END
> RETURN                           1

  内部函数

print_r('i am print_r function.');

  编译后中间代码

function name:  (null)
number of ops:
compiled vars: none
line # * op fetch ext return operands
-------------------------------------------------------------------------------
-
-
> EXT_STMT
EXT_FCALL_BEGIN
SEND_VAL
'i+am+print_r+function.'
DO_FCALL
'print_r'
EXT_FCALL_END
> RETURN

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

if (!is_method && !is_dynamic_fcall && function_name->op_type==IS_CONST) {
opline->opcode = ZEND_DO_FCALL;
opline->op1 = *function_name;
ZVAL_LONG(&opline->op2.u.constant,
zend_hash_func(Z_STRVAL(function_name->u.constant), Z_STRLEN(function_name-
>u.constant) + ));
} else {
opline->opcode = ZEND_DO_FCALL_BY_NAME;
SET_UNUSED(opline->op1);
}

  如果不是方法,并且不是动态调用,并且函数名为字符串变量,则其生成的中间代码为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. hdu acm 2082 找单词

    找单词 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...

  2. TCL:使用、添加库文件

    >直接引用工具自带的库文件 通过指令: .1查看能直接调用的库文件路径 #可以查到工具默认库文件路径,一般包括回显中的路径以及回显中路径的父路径. info library #D:/Script ...

  3. 不常用的toLocaleString()方法

    在javascript中所有的对象都具有toLocaleString().toString()和valueOf()方法. 其中,很多情况调用toString()和valueOf方法会返回相同的值,但是 ...

  4. SQL SERVER 2000数据库置疑处理

    由于服务器意外的断电,导致SQL SERVER服务器上数据库出现“置疑”而无法使用,通过网上搜索,找到以下方法解决问题,这里记录一下: 产生数据库置疑的时侯,数据库文件和日志文件都是存在的,如果数据库 ...

  5. Python 之 super & MRO (没有遇到过适用场景)

    WOW!!! 这里wow的是真尼玛绕且没看完, 好困呐,贴上网址,等自己英文好一点再看(https://rhettinger.wordpress.com/2011/05/26/super-consid ...

  6. 关于通过jq /js 实现验证单选框 复选框是否都有被选中

    今天项目中遇到一个问题 就是要实现,单选框,复选框 同时都被选中才能进行下一步的问题,开始用js原生来写 怎么写都觉得不合适,通过for循环得出 复选框被选中的,在通过for循环得出单选框被选中的,问 ...

  7. java servlet之过滤器1(解决jsp之间POST方式数据传递乱码)

    首先,看看没有解决乱码的效果,新建两个jsp页面(a.jsp跳转到b.jsp). <form action="b.jsp" method="post"&g ...

  8. Xamarin.Android绑定库分享

    使用Xamarin.Android时,会用到各种第三方库,而这些库基本上是java编写的,要在Xamarin.Android中使用这些库,就需要通过Android Binding Project绑定对 ...

  9. 微软四十周年 Microsoft’s 40th anniversary

    比尔-盖茨在4月3日给微软全体员工写了这封邮件,原文是英文,我们翻译了中文.图片是后加上的. 明天将是特殊的一天:微软的40周年纪念日. Tomorrow is a special day: Micr ...

  10. 浅谈Excel开发:九 Excel 开发中遇到的常见问题及解决方法

    Excel开发过程中有时候会遇到各种奇怪的问题,下面就列出一些本人在开发中遇到的一些比较典型的问题,并给出了解决方法,希望对大家有所帮助. 一 插件调试不了以及错误导致崩溃的问题 在开发机器上,有时可 ...