您的位置:首页 > 编程语言 > PHP开发

PHP内核探索笔记-函数

2015-12-15 18:31 941 查看

函数定义:

函数的定义是一个将函数名注册到函数列表的过程

1. 词法分析:

function将会生成T_FUNCTION标记

2. 语法分析:

3. 生成中间代码:

生成的中间代码为 ZEND_DECLARE_FUNCTION ,根据这个中间代码及操作数对应的op_type。 我们可以找到中间代码的执行函数为 ZEND_DECLARE_FUNCTION_SPEC_HANDLER。

在生成中间代码时,已经统一了函数名全部为小写,表示函数的名称不是区分大小写的。

4. 执行中间代码:

执行函数调用函数do_bind_function,在这个函数中将EX(opline)所指向的函数添加到EG(function_table)中,并判断是否已经存在相同名字的函数,如果存在则报错。 EG(function_table)用来存放执行过程中全部的函数信息,相当于函数的注册表。

函数参数

用户自定义函数的参数

在经过词语分析,语法分析后,我们知道对于函数的参数检查是通过 zend_do_receive_arg 函数来实现的。

整个参数的传递是通过给中间代码的arg_info字段执行赋值操作完成。

EG(current_execute_data)。这个变量存放的是当前执行程序或函数的数据。此时我们需要取前一个执行程序的数据,为什么呢? 因为这个函数的调用是在进入函数后执行的

参数的传递

每个PHP脚本都有自己专属的全局符号表,而每个用户自定义的函数也有自己的符号表, 这个符号表用来存储在这个函数作用域下的属于它自己的变量。当调用每个用户自定义的函数时, 都会为这个函数创建一个符号表,当这个函数返回时都会释放这个符号表。

当执行一个拥有参数的用户自定义的函数时,其实它相当于赋值一个操作,即s=a; 只是这个赋值操作的引用计数会执行两次,除了给函数自定义的符号表,还有一个是给函数栈。

参数的传递的第一步是SEND_VAR操作,函数调用是DO_FCALL,在此中间代码之前有一个SEND_VAR操作,此操作的作用是将实参传递给函数, 并且将它添加到函数栈中。

第二步是RECV操作。 RECV操作和SEND_VAR操作不同,它是归属于当前函数的操作,仅为此函数服务。 它的作用是接收SEND过来的变量,并将它们添加到当前函数的符号表。

参数的压栈操作用户自定义的函数和内置函数都需要,而RECV操作仅用户自定义函数需要。

函数的调用和执行

**调用**foo的OPCODE是“DO_FCALL“, DO_FCALL进行函数调用操作时,ZE会在function_table中根据函数名查找函数的定义;

如果存在,就返回该函数zend_function结构指针, 然后通过function.type的值来判断函数是内部函数还是用户定义的函数: 调用zend_execute_internal(zend_internal_function.handler)或者直接 调用zend_execute来执行这个函数包含的zend_op_array。

执行

那么,在函数执行的时候, 进入函数前的环境信息是必须要保存的。在函数执行完毕后,这些环境信息也会被还原,使整个程序继续的执行下去。

内部函数的执行与用户函数不同。用户函数是php语句一条条“翻译”成op_line组成的一个op_array,而内部函数则是用C来实现的,因为执行环境也是C环境。

内部函数和用户函数的处理都是由DO_FCALL来进行的。对于内部函数,zend_execute_internal函数没有定义的情况下,会进行另外一个比较长的调用。

内部函数所在的结构体中 有一个handler指针指向此函数需要调用的内部定义的C函数。 这些内部函数在模块初始化时就以扩展的函数的形式加载到EG(function_table)。

变量函数

$func = 'print_r';


$func(‘i am print_r function.’);

匿名函数

使用create_function()创建”匿名”函数

<?php
$func = create_function('', 'echo "Function created dynamic";');


echo func;//lambda1my_func(); // 不存在这个函数

lambda_1(); // 不存在这个函数

create_function函数的返回值: 函数返回一个唯一的字符串函数名,出现错误的话则返回FALSE。

该函数在定义了一个函数之后,给函数起了个名字,它将函数名的第一个字符变为了’\0’也就是空字符, 然后在函数表中查找是否已经定义了这个函数,如果已经有了则生成新的函数名, 第一个字符为空字符的定义方式比较特殊, 因为在用户代码中无法定义出这样的函数, 也就不存在命名冲突的问题了.

__invoke魔幻方法

?php
class Callme {
public function __invoke($phone_num) {
echo "Hello: $phone_num";
}
}
$call = new Callme();
$call(13810688888); // "Hello: 13810688888


匿名函数的实现

<?php
$func = function() {
echo "Hello, anonymous function";
}
echo gettype($func);    // object
echo get_class($func);  // Closure
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  php 函数 内核