PHP内核探索:定义函数的过程
2011-06-10 00:00
2356 查看
在PHP中,用户函数的定义从function关键字开始。如下所示简单示例:
这是一个非常简单的函数,它所实现的功能是定义一个函数,函数有一个参数,函数的内容是在标准输出端输出传递给它的参数变量的值。
函数的一切从function开始。我们从function开始函数定义的探索之旅。
它所表示的含义是function将会生成T_FUNCTION标记。在获取这个标记后,我们开始语法分析。
关注点在 function is_reference T_STRING,表示function关键字,是否引用,函数名。
T_FUNCTION标记只是用来定位函数的声明,表示这是一个函数,而更多的工作是与这个函数相关的东西,包括参数,返回值等。
生成的中间代码为 ZEND_DECLARE_FUNCTION ,根据这个中间代码及操作数对应的op_type。 我们可以找到中间代码的执行函数为 ZEND_DECLARE_FUNCTION_SPEC_HANDLER。
在生成中间代码时,可以看到已经统一了函数名全部为小写,表示函数的名称不是区分大小写的。
为验证这个实现,我们看一段代码:
执行代码,可以看到屏幕上输出如下报错信息:
表示对于PHP来说T和t是同一个函数名。检验函数名是否重复,这个过程是在哪进行的呢? 下面将要介绍的函数声明中间代码的执行过程包含了这个检查过程。
在这个函数中将EX(opline)所指向的函数添加到EG(function_table)中,并判断是否已经存在相同名字的函数,如果存在则报错。 EG(function_table)用来存放执行过程中全部的函数信息,相当于函数的注册表。 它的结构是一个HashTable,所以在do_bind_function函数中添加新的函数使用的是HashTable的操作函数zend_hash_add。
PHP内核探索:从SAPI接口开始
PHP内核探索:一次请求的开始与结束
PHP内核探索:一次请求生命周期
PHP内核探索:单进程SAPI生命周期
PHP内核探索:多进程/线程的SAPI生命周期
PHP内核探索:Zend引擎
PHP内核探索:再次探讨SAPI
PHP内核探索:Apache模块介绍
PHP内核探索:通过mod_php5支持PHP
PHP内核探索:Apache运行与钩子函数
PHP内核探索:嵌入式PHP
PHP内核探索:PHP的FastCGI
PHP内核探索:如何执行PHP脚本
PHP内核探索:PHP脚本的执行细节
PHP内核探索:操作码OpCode
PHP内核探索:PHP里的opcode
PHP内核探索:解释器的执行过程
PHP内核探索:变量概述
PHP内核探索:变量存储与类型
PHP内核探索:PHP中的哈希表
PHP内核探索:理解Zend里的哈希表
PHP内核探索:PHP哈希算法设计
PHP内核探索:翻译一篇HashTables文章
PHP内核探索:哈希碰撞攻击是什么?
PHP内核探索:常量的实现
PHP内核探索:变量的存储
PHP内核探索:变量的类型
PHP内核探索:变量的值操作
PHP内核探索:变量的创建
PHP内核探索:预定义变量
PHP内核探索:变量的检索
PHP内核探索:变量的类型转换
PHP内核探索:弱类型变量的实现
PHP内核探索:静态变量的实现
PHP内核探索:变量类型提示
PHP内核探索:变量的生命周期
PHP内核探索:变量赋值与销毁
PHP内核探索:变量作用域
PHP内核探索:诡异的变量名
PHP内核探索:变量的value和type存储
PHP内核探索:全局变量Global
PHP内核探索:变量类型的转换
PHP内核探索:内存管理开篇
PHP内核探索:Zend内存管理器
PHP内核探索:PHP的内存管理
PHP内核探索:内存的申请与销毁
PHP内核探索:引用计数与写时复制
PHP内核探索:PHP5.3的垃圾回收机制
PHP内核探索:内存管理中的cache
PHP内核探索:写时复制COW机制
PHP内核探索:数组与链表
PHP内核探索:使用哈希表API
PHP内核探索:数组操作
PHP内核探索:数组源码分析
PHP内核探索:函数的分类
PHP内核探索:函数的内部结构
PHP内核探索:函数结构转换
PHP内核探索:定义函数的过程
PHP内核探索:函数的参数
PHP内核探索:zend_parse_parameters函数
PHP内核探索:函数返回值
PHP内核探索:形参return value
PHP内核探索:函数调用与执行
PHP内核探索:引用与函数执行
PHP内核探索:匿名函数及闭包
PHP内核探索:面向对象开篇
PHP内核探索:类的结构和实现
PHP内核探索:类的成员变量
PHP内核探索:类的成员方法
PHP内核探索:类的原型zend_class_entry
PHP内核探索:类的定义
PHP内核探索:访问控制
PHP内核探索:继承,多态与抽象类
PHP内核探索:魔术函数与延迟绑定
PHP内核探索:保留类与特殊类
PHP内核探索:对象
PHP内核探索:创建对象实例
PHP内核探索:对象属性读写
PHP内核探索:命名空间
PHP内核探索:定义接口
PHP内核探索:继承与实现接口
PHP内核探索:资源resource类型
PHP内核探索:Zend虚拟机
PHP内核探索:虚拟机的词法解析
PHP内核探索:虚拟机的语法分析
PHP内核探索:中间代码opcode的执行
PHP内核探索:代码的加密与解密
PHP内核探索:zend_execute的具体执行过程
PHP内核探索:变量的引用与计数规则
PHP内核探索:新垃圾回收机制说明
function foo($var) { echo $var; }
这是一个非常简单的函数,它所实现的功能是定义一个函数,函数有一个参数,函数的内容是在标准输出端输出传递给它的参数变量的值。
函数的一切从function开始。我们从function开始函数定义的探索之旅。
词法分析
在 Zend/zend_language_scanner.l中我们找到如下所示的代码:"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 1; } function t() { 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。
延伸阅读
此文章所在专题列表如下:PHP内核探索:从SAPI接口开始
PHP内核探索:一次请求的开始与结束
PHP内核探索:一次请求生命周期
PHP内核探索:单进程SAPI生命周期
PHP内核探索:多进程/线程的SAPI生命周期
PHP内核探索:Zend引擎
PHP内核探索:再次探讨SAPI
PHP内核探索:Apache模块介绍
PHP内核探索:通过mod_php5支持PHP
PHP内核探索:Apache运行与钩子函数
PHP内核探索:嵌入式PHP
PHP内核探索:PHP的FastCGI
PHP内核探索:如何执行PHP脚本
PHP内核探索:PHP脚本的执行细节
PHP内核探索:操作码OpCode
PHP内核探索:PHP里的opcode
PHP内核探索:解释器的执行过程
PHP内核探索:变量概述
PHP内核探索:变量存储与类型
PHP内核探索:PHP中的哈希表
PHP内核探索:理解Zend里的哈希表
PHP内核探索:PHP哈希算法设计
PHP内核探索:翻译一篇HashTables文章
PHP内核探索:哈希碰撞攻击是什么?
PHP内核探索:常量的实现
PHP内核探索:变量的存储
PHP内核探索:变量的类型
PHP内核探索:变量的值操作
PHP内核探索:变量的创建
PHP内核探索:预定义变量
PHP内核探索:变量的检索
PHP内核探索:变量的类型转换
PHP内核探索:弱类型变量的实现
PHP内核探索:静态变量的实现
PHP内核探索:变量类型提示
PHP内核探索:变量的生命周期
PHP内核探索:变量赋值与销毁
PHP内核探索:变量作用域
PHP内核探索:诡异的变量名
PHP内核探索:变量的value和type存储
PHP内核探索:全局变量Global
PHP内核探索:变量类型的转换
PHP内核探索:内存管理开篇
PHP内核探索:Zend内存管理器
PHP内核探索:PHP的内存管理
PHP内核探索:内存的申请与销毁
PHP内核探索:引用计数与写时复制
PHP内核探索:PHP5.3的垃圾回收机制
PHP内核探索:内存管理中的cache
PHP内核探索:写时复制COW机制
PHP内核探索:数组与链表
PHP内核探索:使用哈希表API
PHP内核探索:数组操作
PHP内核探索:数组源码分析
PHP内核探索:函数的分类
PHP内核探索:函数的内部结构
PHP内核探索:函数结构转换
PHP内核探索:定义函数的过程
PHP内核探索:函数的参数
PHP内核探索:zend_parse_parameters函数
PHP内核探索:函数返回值
PHP内核探索:形参return value
PHP内核探索:函数调用与执行
PHP内核探索:引用与函数执行
PHP内核探索:匿名函数及闭包
PHP内核探索:面向对象开篇
PHP内核探索:类的结构和实现
PHP内核探索:类的成员变量
PHP内核探索:类的成员方法
PHP内核探索:类的原型zend_class_entry
PHP内核探索:类的定义
PHP内核探索:访问控制
PHP内核探索:继承,多态与抽象类
PHP内核探索:魔术函数与延迟绑定
PHP内核探索:保留类与特殊类
PHP内核探索:对象
PHP内核探索:创建对象实例
PHP内核探索:对象属性读写
PHP内核探索:命名空间
PHP内核探索:定义接口
PHP内核探索:继承与实现接口
PHP内核探索:资源resource类型
PHP内核探索:Zend虚拟机
PHP内核探索:虚拟机的词法解析
PHP内核探索:虚拟机的语法分析
PHP内核探索:中间代码opcode的执行
PHP内核探索:代码的加密与解密
PHP内核探索:zend_execute的具体执行过程
PHP内核探索:变量的引用与计数规则
PHP内核探索:新垃圾回收机制说明
相关文章推荐
- 深入理解PHP内核(十二)函数-函数的定义、传参及返回值
- PHP内核探索 —— 预定义变量:PHP脚本运行前将一些特殊变量加到符号表
- 深入理解PHP内核(六)函数的定义、传参及返回值
- PHP内核探索:魔术函数与延迟绑定
- PHP内核探索 —— 变量的检索:zend_hash_find()函数
- PHP内核探索之解释器的执行过程
- PHP内核探索:预定义变量
- PHP内核探索 —— 解释器的执行过程:引擎是如何执行PHP代码的
- PHP内核探索:函数的内部结构
- PHP内核探索:解释器的执行过程
- PHP内核探索:函数结构转换
- PHP内核探索:函数调用与执行
- 17.PHP内核探索:解释器的执行过程
- PHP内核探索:函数的分类
- PHP内核探索:函数的参数
- PHP内核探索:引用与函数执行
- 原:PHP内核研究 函数的定义
- PHP内核探索:函数返回值
- PHP内核探索:定义接口
- PHP内核探索:zend_execute的具体执行过程