58-定义函数的过程

原创 2016年04月26日 12:32:37

58-定义函数的过程

在PHP中,用户函数的定义从function关键字开始。如下所示简单示例:

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。

Delphi 过程与函数

1.标准过程与标准函数         过程和函数是实现一定功能的语句块,是程序中的特定功能单元,即子程序。         Delphi语言的过程有两种:标准过程与自定义过程。标准过程是系统内部...
  • hailmy
  • hailmy
  • 2013年09月17日 13:45
  • 1280

Oracle的plsql 函数、存储过程

---存储函数 --格式 --函数的声明(有参数的写在小括号里) create or replace function func_name(v_param varchar2) --返回值类型 re...
  • weisg81
  • weisg81
  • 2015年05月16日 23:13
  • 1903

PostgreSQL 存储过程与函数

wangzhiqing999 PostgreSQL 存储过程与函数 创建一个存储过程PostgreSQL 好像没有专门的 CREATE OR REPLACE PROCEDURE 全部都是 FUNC...
  • thinkandaction
  • thinkandaction
  • 2014年03月16日 20:37
  • 1231

Oracle数据库之PL/SQL过程与函数

Oracle数据库之PL/SQL过程与函数PL/SQL块分为匿名块与命名块,命名块又包含子程序、包和触发器。过程和函数统称为PL/SQL子程序,我们可以将商业逻辑、企业规则写成过程或函数保存到数据库中...
  • zhliro
  • zhliro
  • 2015年06月17日 14:00
  • 1842

SQL中存储过程和自定义函数的区别

SQL中存储过程和自定义函数的区别 存储过程: 存储过程可以使得对数据库的管理、以及显示关于数据库及其用户信息的工作容易得多。存储过程是 SQL 语句和可选控制流语句的预编译集合,以一个名称存储并...
  • u011411356
  • u011411356
  • 2015年11月30日 16:05
  • 1411

pb使用RPCFUNC调用存储过程

归纳起来PB6.0中调用Oracle存储过程有以下四种方法。   方法一:以关键字RPCFUNC声明存储过程;   方法二:以DECLARE PROCEDURE语句声明存储过程;   方法三:以动态S...
  • pcwe2002
  • pcwe2002
  • 2016年09月26日 22:56
  • 1179

Oracle存储过程和自定义函数

概述PL/SQL中的过程和函数(通常称为子程序)是PL/SQL块的一种特殊的类型,这种类型的子程序可以以编译的形式存放在数据库中,并为后续的程序块调用。相同点: 完成特定功能的程序 不同点:是否用r...
  • yangshangwei
  • yangshangwei
  • 2016年06月03日 23:29
  • 12089

JavaScript几种函数声明方式的区别

函数声明方式  1.函数声明方式  其语法为:  Java代码   function 函数名(参数1,参数2,...){       //要执行的语句   }   例1:  Jav...
  • ll641058431
  • ll641058431
  • 2016年08月25日 23:58
  • 3942

随机过程基本概念

翻译自 Kalman R E. A New Approach to Linear Filtering and Prediction Problems[J]. 1960, 82D:35-45.随机过程...
  • xtydtc
  • xtydtc
  • 2017年05月04日 13:55
  • 1223

ORACLE创建包和包体及包中函数和存储过程

创建包声明的过程脚本: CREATE OR REPLACE PACKAGE "COM_BOS" is ---------------------------------------------...
  • whb123andy
  • whb123andy
  • 2013年12月03日 09:15
  • 3609
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:58-定义函数的过程
举报原因:
原因补充:

(最多只允许输入30个字)