PHP内核探索笔记-函数

函数定义:

函数的定义是一个将函数名注册到函数列表的过程
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;//lambda1 func(); // Function created dynamic
myfunc=lambda1; my_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,函数,内核)