在PHP中,用户函数的定义从function关键字开始。如下所示简单示例:
function foo($var) {
[echo](http://www.php.net/echo) $var;
}
这是一个非常简单的函数,它所实现的功能是定义一个函数,函数有一个参数,函数的内容是在标准输出端输出传递给它的参数变量的值。
函数的一切从function开始。我们从function开始函数定义的探索之旅。
**词法分析**
在 Zend/zend_language_scanner.l中我们找到如下所示的代码:
<ST_IN_SCRIPTING>"function" {
return T_FUNCTION;
}
它所表示的含义是function将会生成T_FUNCTION标记。在获取这个标记后,我们开始语法分析。
**语法分析**
在 Zend/zend_language_parser.y文件中找到函数的声明过程标记如下:
function:
T_FUNCTION { $$.u.opline_num = CG(zend_lineno); }
;
is_reference:
/* empty */ { $$.op_type = ZEND_RETURN_VAL; }
| '&' { $$.op_type = ZEND_RETURN_REF; }
;
unticked_function_declaration_statement:
function is_reference T_STRING {
zend_do_begin_function_declaration(&$1, &$3, 0, $2.op_type, NULL TSRMLS_CC); }
'(' parameter_list ')' '{' inner_statement_list '}' {
zend_do_end_function_declaration(&$1 TSRMLS_CC); }
;
> 关注点在 function is_reference T_STRING,表示function关键字,是否引用,函数名。
T_FUNCTION标记只是用来定位函数的声明,表示这是一个函数,而更多的工作是与这个函数相关的东西,包括参数,返回值等。
**生成中间代码**
语法解析后,我们看到所执行编译函数为zend_do_begin_function_declaration。在 Zend/zend_complie.c文件中找到其实现如下:
void zend_do_begin_function_declaration(znode *function_token, znode *function_name,
int is_method, int return_reference, znode *fn_flags_znode TSRMLS_DC) /* {{{ */
{
...//省略
function_token->u.op_array = CG(active_op_array);
lcname = zend_str_tolower_dup(name, name_len);
orig_interactive = CG(interactive);
CG(interactive) = 0;
init_op_array(&op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE TSRMLS_CC);
CG(interactive) = orig_interactive;
...//省略
if (is_method) {
...//省略 类方法 在后面的类章节介绍
} else {
zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
opline->opcode = ZEND_DECLARE_FUNCTION;
opline->op1.op_type = IS_CONST;
build_runtime_defined_function_key(&opline->op1.u.constant, lcname,
name_len TSRMLS_CC);
opline->op2.op_type = IS_CONST;
opline->op2.u.constant.type = IS_STRING;
opline->op2.u.constant.value.str.val = lcname;
opline->op2.u.constant.value.str.len = name_len;
Z_SET_REFCOUNT(opline->op2.u.constant, 1);
opline->extended_value = ZEND_DECLARE_FUNCTION;
zend_hash_update(CG(function_table), opline->op1.u.constant.value.str.val,
opline->op1.u.constant.value.str.len, &op_array, sizeof(zend_op_array),
(void **) &CG(active_op_array));
}
}
/* }}} */
生成的中间代码为 **ZEND_DECLARE_FUNCTION** ,根据这个中间代码及操作数对应的op_type。我们可以找到中间代码的执行函数为 **ZEND_DECLARE_FUNCTION_SPEC_HANDLER**。
> 在生成中间代码时,可以看到已经统一了函数名全部为小写,表示函数的名称不是区分大小写的。
为验证这个实现,我们看一段代码:
function T() {
[echo](http://www.php.net/echo) 1;
}
function t() {
[echo](http://www.php.net/echo) 2;
}
执行代码,可以看到屏幕上输出如下报错信息:
Fatal error: Cannot redeclare t() (previously declared in ...)
表示对于PHP来说T和t是同一个函数名。检验函数名是否重复,这个过程是在哪进行的呢?下面将要介绍的函数声明中间代码的执行过程包含了这个检查过程。
**执行中间代码**
在 Zend/zend_vm_execute.h 文件中找到 ZEND_DECLARE_FUNCTION中间代码对应的执行函数:ZEND_DECLARE_FUNCTION_SPEC_HANDLER。此函数只调用了函数do_bind_function。其调用代码为:
do_bind_function(EX(opline), EG(function_table), 0);
在这个函数中将EX(opline)所指向的函数添加到EG(function_table)中,并判断是否已经存在相同名字的函数,如果存在则报错。EG(function_table)用来存放执行过程中全部的函数信息,相当于函数的注册表。它的结构是一个HashTable,所以在do_bind_function函数中添加新的函数使用的是HashTable的操作函数**zend_hash_add**
- 第一章 准备工作和背景知识
- 第一节 环境搭建
- 第二节 源码结构、阅读代码方法
- 第三节 常用代码
- 第四节 小结
- 第二章 用户代码的执行
- 第一节 生命周期和Zend引擎
- 第二节 SAPI概述
- Apache模块
- 嵌入式
- FastCGI
- 第三节 PHP脚本的执行
- 词法分析和语法分析
- opcode
- opcode处理函数查找
- 第四节 小结
- 第三章 变量及数据类型
- 第一节 变量的结构和类型
- 哈希表(HashTable)
- PHP的哈希表实现
- 链表简介
- 第二节 常量
- 第三节 预定义变量
- 第四节 静态变量
- 第五节 类型提示的实现
- 第六节 变量的生命周期
- 变量的赋值和销毁
- 变量的作用域
- global语句
- 第七节 数据类型转换
- 第八节 小结
- 第四章 函数的实现
- 第一节 函数的内部结构
- 函数的内部结构
- 函数间的转换
- 第二节 函数的定义,传参及返回值
- 函数的定义
- 函数的参数
- 函数的返回值
- 第三节 函数的调用和执行
- 第四节 匿名函数及闭包
- 第五节 小结
- 第五章 类和面向对象
- 第一节 类的结构和实现
- 第二节 类的成员变量及方法
- 第三节 访问控制的实现
- 第四节 类的继承,多态及抽象类
- 第五节 魔术方法,延迟绑定及静态成员
- 第六节 PHP保留类及特殊类
- 第七节 对象
- 第八节 命名空间
- 第九节 标准类
- 第十节 小结
- 第六章 内存管理
- 第一节 内存管理概述
- 第二节 PHP中的内存管理
- 第三节 内存使用:申请和销毁
- 第四节 垃圾回收
- 新的垃圾回收
- 第五节 内存管理中的缓存
- 第六节 写时复制(Copy On Write)
- 第七节 内存泄漏
- 第八节 小结
- 第七章 Zend虚拟机
- 第一节 Zend虚拟机概述
- 第二节 语法的实现
- 词法解析
- 语法分析
- 实现自己的语法
- 第三节 中间代码的执行
- 第四节 PHP代码的加密解密
- 第五节 小结
- 第八章 线程安全
- 第二节 线程,进程和并发
- 第三节 PHP中的线程安全
- 第九章 错误和异常处理
- 第十章 输出缓冲
- 第十六章 PHP语言特性的实现
- 第一节 循环语句
- foreach的实现
- 第二十章 怎么样系列(how to)
- 附录
- 附录A PHP及Zend API
- 附录B PHP的历史
- 附录C VLD扩展使用指南
- 附录D 怎样为PHP贡献
- 附录E phpt测试文件说明
- 附录F PHP5.4新功能升级解析
- 附录G:re2c中文手册