威尼斯手机平台登陆-官方网站登录

威尼斯手机平台登陆为您带来世界三大博彩公司最新相关资讯,威尼斯官方网站登录充分考虑到不同地域网民的不同需求,威尼斯手机平台登陆良好的用户界面,人性化的操作,实用的功能设计使其广泛受到欢迎,推动实体出版、影视、动漫、游戏等相关文化产业的发展。

您的位置:威尼斯手机平台登陆 > 最新留言 > 函数的回到值类型应该是规定不改变的,  顾客函数的定义从function 关键字初叶

函数的回到值类型应该是规定不改变的,  顾客函数的定义从function 关键字初叶

发布时间:2020-03-04 19:26编辑:最新留言浏览(163)

    在付出进度中,函数的归来值类型应该是显著不改变的,但PHP是弱类型的言语,

    一、函数的定义

    于是PHP是向来不此类语法验证的,正因为如此,形成了不菲坑坑。

      客户函数的定义从function 关键字开头,如下

    比方说下边包车型客车代码:

    function foo($var) {
        echo $var;
    }
    

    <?php
    function getArticles(…){
    $arrData = array();
    if($exp1){
    return $arrData;
    }else if($exp2){
    return 1;
    }else{
    return false;
    }

    }
    $arrData =getArticles(…);
    foreach($arrData as $record){
    //do something.
    ….
    }
    ?>

      1、词法分析

    函数getArticles依据分歧的条件再次来到不一致品种的值,有bool、int、还会有数组,符合规律状态那类函数是意在再次回到数组,然后拿数组去做一些此外操作,

      在Zend/zend_language_scanner.l中我们找到如下所示的代码:

    可因为函数重返值类型不定点,调用时就很只怕发生各类预想不到的坑,

    <ST_IN_SCRIPTING>"function" {
        return T_FUNCTION;
    }
    

    之所以作者就想,既然无法标准,那直接强逼好了。

      它所表示的意思是function将会生成T_FUNCTION标识。在获得这一个符号后,大家开首语法解析。

    函数/方法重回值能够强逼类型,如 图

      2、语法剖析

    图片 1

      在Zend/zend_language_parser.y文件中找到函数的申明进程标识如下:

    援救种种强制类型节制:int、array、bool、object,当重临值与函数评释中的类型不匹配时,抛出warning,本来想抛出error,不过感觉

    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); }
    ;
    

    太残暴了,只好算是个拾贰分,算不上错误,所以就用warning好了。

        关切点在function is_reference T_ST传祺ING,表示function关键字,是不是引用,函数名

    PHP自己是不援助 int function 那样的语法的,所以要支持,就先要化解语法解析器,关于语法分析器,能够运动这里>>>查看

      T_FUNCTION标识只是用来牢固函数的注脚,表示那是八个函数,而更加多的做事是与这些函数相关的事物,包罗参数,再次来到值。

    实际情况,这里就不讲了,

      3、生成人中学间代码

    先改过语法扫描 Zend/zend_language_scanner.l文件

      语法拆解解析后,大家见到所试行编写翻译函数为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) {
            ...//省略,类方法 在后面的章节介绍
    „!ǶGH
        } 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));
        }
    
    }
    /* }}} */
    

    <ST_IN_SCRIPTING>”int” {
    return T_FUNCTION_RETURN_INT;
    }
    <ST_IN_SCRIPTING>”bool” {
    return T_FUNCTION_RETURN_OBJECT;
    }
    <ST_IN_SCRIPTING>”object” {
    return T_FUNCTION_RETURN_OBJECT;
    }
    <ST_IN_SCRIPTING>”resource” {
    return T_FUNCTION_RETURN_RESOURCE;
    }

      生成的代码为ZEND_DECLARE_FUNCTION,根据那几个个中的代码及操作数对应的op_type。大家能够找到中间代码的实施函数为ZEND_DECLARE_FUNCTION_SPEC_HANDLER。

    情趣十分轻便,扫描器扫描到到重大字 int、bool、object、resource、array时回来相应的T_FUNCTION_* ,那是二个token,

        在转移中间代码的时候,能够见到曾经济同盟并了函数名全副为小写,表示函数的名目不是区  分大小写的。

    scanner根据分歧的token做分歧的拍卖,token要先在Zend/zend_language_parser.y文件中定义

      为评释那么些实现,大家看一段代码

    追加如下代码

    function T() {
        echo 1;
    }
    
    function t() {
        echo 2;
    }
    

    ……….
    %token T_FUNCTION_RETURN_INT
    %token T_FUNCTION_RETURN_BOOL
    %token T_FUNCTION_RETURN_STRING
    %token T_FUNCTION_RETURN_OBJECT
    %token T_FUNCTION_RETURN_RESOURCE
    1

    接下来增添token管理逻辑:

    1
    function:
    T_FUNCTION { $$.u.opline_num = CG(zend_lineno);$$.u.EA.var  = 0; }
    |   T_FUNCTION_RETURN_INT T_FUNCTION {
    $$.u.opline_num = CG(zend_lineno);
    $$.u.EA.var = IS_LONG;
    }
    |   T_FUNCTION_RETURN_BOOL T_FUNCTION {
    $$.u.opline_num = CG(zend_lineno);
    $$.u.EA.var = IS_BOOL;
    }
    |   T_FUNCTION_RETURN_STRING T_FUNCTION {
    $$.u.opline_num = CG(zend_lineno);
    $$.u.EA.var = IS_STRING;
    }
    |   T_FUNCTION_RETURN_OBJECT T_FUNCTION {
    $$.u.opline_num = CG(zend_lineno);
    $$.u.EA.var = IS_OBJECT;
    }
    |   T_FUNCTION_RETURN_RESOURCE T_FUNCTION {
    $$.u.opline_num = CG(zend_lineno);
    $$.u.EA.var = IS_RESOURCE;
    }
    |   T_ARRAY T_FUNCTION {
    $$.u.opline_num = CG(zend_lineno);
    $$.u.EA.var = IS_ARRAY;
    }

      实行代码会报错Fatal error: Cannot redeclare t(卡塔尔(قطر‎ (previously declared in ...State of Qatar

    $$.u.EA.var 存款和储蓄的是 函数再次来到类型,最终要拿他来跟回来值类型做协作,

      表示对此PHP来讲T和t是同三个函数名,校验函数名是不是再一次,这一个进度是在哪进行的吗?

    如此那般语法解释器就可以拍卖我们新的php语法了。

      4、实行中间代码

    那还非常不够,还索要修改函数评释定义的拍卖逻辑

      在Zend/zend_vm_execute.h文件中找到ZEND_DECLARE_FUNCTION中间代码对应的施行函数:ZEND_DECLARE_FUNCTION_SPEC_HANDLELX570。此函数只调用了函数do_bind_function。其调用代码为:

    Zend/zend_compile.c ::zend_do_begin_function_declaration

    ……
    zend_op_array op_array;
    char *name = function_name->u.constant.value.str.val;
    int name_len = function_name->u.constant.value.str.len;
    int function_type  = function_token->u.EA.var; //保存函数类型,在语法解释器中扩大的: $$.u.EA.var = IS_LONG;
    int function_begin_line = function_token->u.opline_num;
    ……
    op_array.function_name = name;
    op_array.fn_type = function_type; //将类型保存到op_array中,
    op_array.return_reference = return_reference;
    op_array.fn_flags |= fn_flags;
    op_array.pass_rest_by_reference = 0;
    ……….

    do_bind_function(EX(opline), EG(function_table), 0);
    

    PHP是先深入分析PHP语法生成对应的opcode,将须要的条件、参数消息保存到execute_data全局变量中,最终在通过execute函数逐个实施opcode,

      在此个函数上校EX(oplineState of Qatar所指向的函数增添到EG(function_tableState of Qatar中,并认清是不是早就存在相仿名字的函数,假诺存在则报错,EG(function_table卡塔尔用来存放在实行进度中全数的函数新闻,约等于函数的注册表。它的构造是二个HashTable,所以在do_bind_function函数中加多新的函数使用的是HashTable的操作函数zend_hash_add

    故此要做管理就要把函数的品类保存到opcode中:op_array.fn_type = function_type;

     

    op_array是没有fn_type的,要修改op_array的结构,增加zend_uint fn_type;

    二、函数的参数

    (关于opcode你能够想像一下 从c转为汇编,作者博客中也会有连带小说,能够参照一下)

      函数的概念只是二个将函数名注册到函数列表的历程。

    最终要校正opcode的毁掉函数,函数的回到 return 会生成token T_RETURN,T_RETUXC90N会依据重回的品种调用差异的calback函数:

      1、客商自定义函数的参数

    ZEND_RETURN_SPEC_CONST_HANDLER
    ZEND_RETURN_SPEC_TMP_HANDLER
    ZEND_RETURN_SPEC_VAR_HANDLER

      大家知晓对于函数的参数检查是经过zend_do_receive_arg函数来达成的,在这里函数中对此参数的第一代码如下:

    它有多少个callback,要是重回值是一个 const类型的数量,则 ZEND_RETURN_SPEC_CONST_HANDLER
    再次回到值是一时数据,如 : return 1,则ZEND_RETURN_SPEC_TMP_HANDLER
    再次回到值是三个变量,如 : return $a,则ZEND_RETURN_SPEC_VAR_HANDLER

    CG(active_op_array)->arg_info = erealloc(CG(active_op_array)->arg_info,
            sizeof(zend_arg_info)*(CG(active_op_array)->num_args));
    cur_arg_info = &CG(active_op_array)->arg_info[CG(active_op_array)->num_args-1];
    cur_arg_info->name = estrndup(varname->u.constant.value.str.val,
            varname->u.constant.value.str.len);
    cur_arg_info->name_len = varname->u.constant.value.str.len;
    cur_arg_info->array_type_hint = 0;
    cur_arg_info->allow_null = 1;
    cur_arg_info->pass_by_reference = pass_by_reference;
    cur_arg_info->class_name = NULL;
    cur_arg_info->class_name_len = 0;
    

    因而要在这里八个callback函数中加进管理逻辑:

      整个参数的传递是通过给中间代码的arg_info字段推行赋值操作实现。关键点是在arg_info字段,arg_info字段的布局如下:

    在callback函数return以前扩大如下代码

    typedef struct _zend_arg_info {
        const char *name;   /*参数的名称*/
        zend_uint name_len;     /*参数名称的长度*/
        const char *class_name; /* 类名*/
         zend_uint class_name_len;   /*类名长度*/
        zend_bool array_type_hint;  /*数组类型提示*/
        zend_bool allow_null;   /*是否允许为NULLͺ*/
        zend_bool pass_by_reference;    /*是否引用传递*/
        zend_bool return_reference; 
        int required_num_args;  
    } zend_arg_info;
    

    if((EG(active_op_array)->fn_type > 0) && Z_TYPE_P(retval_ptr) != EG(active_op_array)->fn_type){
    php_error_docref0(NULL TSRMLS_DC,E_WARNING, “function name %s return a wrong type.”, EG(active_op_array)->function_name );
    }

      参数的值传递和参数字传送递的界别是通过pass_by_reference参数在转移中间代码时落到实处的。

    fn_type 去跟 再次来到值的类型作相比,若无相称到,就能抛出那么些warning。

      对于参数的个数,中间代码中包括的arg_nums字段在每一回实行**zend_do_receive_argxx时都会加1.之类代码:

    作者已经打了补丁,近年来只支持php5.3版本,有亟待的能够拿去玩一玩。

    CG(active_op_array)->num_args++;
    

    不晓得怎么官方不帮忙此语法,作者认为依然挺有要求的。

      何况当前参数的目录为ŒCG(active_op_array)->num_args-1.如下代码:

    下载补丁:php-syntax.patch

    cur_arg_info = &CG(active_op_array)->arg_info[CG(active_op_array)->num_args-1];
    

      以上的剖析是本着函数定义时的参数设置,那个参数是定位的。而在实际上编写程序时可能大家会用到可变参数。当时我们会用到函数func_num_args和func_get_args。它们是以内部函数存在。于是在Zendzend_builtin_functions.c文件中找到那三个函数的落到实处。大家先是来看func_num_args函数的达成,其代码如下:

    /* {{{ proto int func_num_args(void)
       Get the number of arguments that were passed to the function */
    ZEND_FUNCTION(func_num_args)
    {
        zend_execute_data *ex = EG(current_execute_data)->prev_execute_data;
    
        if (ex && ex->function_state.arguments) {
            RETURN_LONG((long)(zend_uintptr_t)*(ex->function_state.arguments));
        } else {
            zend_error(E_WARNING,
    "func_num_args():  Called from the global scope - no function context");
            RETURN_LONG(-1);
        }
    }
    /* }}} */
    

      在存在ex->function_state.arguments的境况下,及函数调用时,重临ex->function_state.arguments转变后的值,否则显示错误并回到-1。这里最器重的少数是EG(current_execute_dataState of Qatar。这一个变量寄放的是近年来进行顺序或函数的数目,此时大家必要取前一个施行顺序的数码,为啥吗?因为这几个函数的调用是在步入函数后试行的。函数的有关数据等都在前头施行进程中,于是调用的是:

    zend_execute_data *ex = EG(current_execute_data)->prev_execute_data;
    

     

      2、内部函数的参数

      以宽广的count函数为例,其参数管理部分的代码如下:

    /* {{{ proto int count(mixed var [, int mode])
       Count the number of elements in a variable (usually an array) */
    PHP_FUNCTION(count)
    {
        zval *array;
        long mode = COUNT_NORMAL;
    
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l",
             &array, &mode) == FAILURE) {
            return;
        }
        ... //省略
    }
    

      这里富含了多个操作:贰个是取参数的个数,一个是解析参数列表。

      (1State of Qatar取参数的个数

      取参数的个数是经过ZEND_NUM_A哈弗GS(State of Qatar宏来达成的,其定义如下:

    #define ZEND_NUM_ARGS()     (ht)
    

      ht是在Zend/zend.h文件中定义的宏INTEHavalNAL_FUNCTION_PARAMETERS中的ht,如下

    #define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value,
    zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC
    

      (2State of Qatar深入解析参数列表

      PHP内部函数在言之有序参数时选用的是zend_parse_parameters。它能够大大简化参数的接受处总管业,即便它在拍卖可变参数时还会有一些弱。

      其注脚如下:

    ZEND_API int zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, 
    ...)
    
    • 先是个参数num_args表明表示想要采纳的参数个数,大家日常应用ZEND_NUM_A牧马人GS(卡塔尔(قطر‎来表示对传播的参数“有多少要稍稍”
    • 其次个参数应该是宏TSRMLS_CC。
    • 其五个参数type_spec是三个字符串,用来钦赐大家所期待接纳的次第参数的花色,有一点点相仿于printf中内定输出格式的不胜格式化字符串。
    • 结余的参数正是我们用来选拔PHP参数值的变量的指针。

      zend_parse_parameters(卡塔尔国在分析参数的还要户尽可能的改动参数类型,那样就能够有限支撑我们总是能博得所希望的品类的变量

     

      3、函数的重回值

      PHP中等学园函授数都有重返值,没return重回null

      (1)return语句

      从Zend/zend_language_parser.y文件中能够肯定其变动中间代码调用的是zend_do_return函数。

    void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */
    {
        zend_op *opline;
        int start_op_number, end_op_number;
     if (do_end_vparse) {
            if (CG(active_op_array)->return_reference
                    && !zend_is_function_or_method_call(expr)) {
                zend_do_end_variable_parse(expr, BP_VAR_W, 0 TSRMLS_CC);/* 处理返回引用 */
            } else {
                zend_do_end_variable_parse(expr, BP_VAR_R, 0 TSRMLS_CC);/* 处理常规变量返回 */
            }
        }
    
       ...// 省略,取其他中间代码操作
    
        opline->opcode = ZEND_RETURN;
    
        if (expr) {
            opline->op1 = *expr;
    
            if (do_end_vparse && zend_is_function_or_method_call(expr)) {
                opline->extended_value = ZEND_RETURNS_FUNCTION;
            }
        } else {
            opline->op1.op_type = IS_CONST;
            INIT_ZVAL(opline->op1.u.constant);
        }
    
        SET_UNUSED(opline->op2);
    }
    /* }}} */
    

      生成人中学间代码为ZEND_RETU揽胜N。第三个操作数的品种在再次回到值为可用的表达式时,其类别为表明式的操作类型,不然类型为IS_CONST。那在延续总结实施中间代码函数时有用到。依照操作数的不等,ZEND_RETU君越N中间代码会施行ZEND_RETURN_SPEC_CONST_HANDLER,ZEND_RETURN_SPEC_TMP_HANDLER或ZEND_RETURN_SPEC_TMP_HANDLE库罗德。那八个函数的实施流程基本贴近,包罗对部分不当的拍卖。这里大家以ZEND_RETURN_SPEC_CONST_HANDLEHaval为例表达函数重回值的实践进度:

    static int ZEND_FASTCALL  
    ZEND_RETURN_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
    {
        zend_op *opline = EX(opline);
        zval *retval_ptr;
        zval **retval_ptr_ptr;
    
    
        if (EG(active_op_array)->return_reference == ZEND_RETURN_REF) {
    
            //  ǓǔŷsÁɁƶMļ@ɗÁĻļ
            if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) {   
                /* Not supposed to happen, but we'll allow it */
                zend_error(E_NOTICE, "Only variable references 
                    should be returned by reference");
                goto return_by_value;
            }
    
            retval_ptr_ptr = NULL;  //  ǓǔŔ
    
            if (IS_CONST == IS_VAR && !retval_ptr_ptr) {
                zend_error_noreturn(E_ERROR, "Cannot return string offsets by 
    reference");
            }
     if (IS_CONST == IS_VAR && !Z_ISREF_PP(retval_ptr_ptr)) {
                if (opline->extended_value == ZEND_RETURNS_FUNCTION &&
                    EX_T(opline->op1.u.var).var.fcall_returned_reference) {
                } else if (EX_T(opline->op1.u.var).var.ptr_ptr ==
                        &EX_T(opline->op1.u.var).var.ptr) {
                    if (IS_CONST == IS_VAR && !0) {
                          /* undo the effect of get_zval_ptr_ptr() */
                        PZVAL_LOCK(*retval_ptr_ptr);
                    }
                    zend_error(E_NOTICE, "Only variable references 
                     should be returned by reference");
                    goto return_by_value;
                }
            }
    
            if (EG(return_value_ptr_ptr)) { //  Ǔǔŷs
                SEPARATE_ZVAL_TO_MAKE_IS_REF(retval_ptr_ptr);   //  is_ref__gcőęŒ
    1
                Z_ADDREF_PP(retval_ptr_ptr);    //  refcount__gcŒď×1
    
                (*EG(return_value_ptr_ptr)) = (*retval_ptr_ptr);
            }
        } else {
    return_by_value:
    
            retval_ptr = &opline->op1.u.constant;
    
            if (!EG(return_value_ptr_ptr)) {
                if (IS_CONST == IS_TMP_VAR) {
    
                }
            } else if (!0) { /* Not a temp var */
                if (IS_CONST == IS_CONST ||
                    EG(active_op_array)->return_reference == ZEND_RETURN_REF ||
                    (PZVAL_IS_REF(retval_ptr) && Z_REFCOUNT_P(retval_ptr) > 0)) {
                    zval *ret;
    
                    ALLOC_ZVAL(ret);
                    INIT_PZVAL_COPY(ret, retval_ptr);   //  Ł™ͿʍǓǔŔ 
                    zval_copy_ctor(ret);
                    *EG(return_value_ptr_ptr) = ret;
                } else {
                    *EG(return_value_ptr_ptr) = retval_ptr; //  ħ6ɶŔ
                    Z_ADDREF_P(retval_ptr);
                }
            } else {
                zval *ret;
    
                ALLOC_ZVAL(ret);
                INIT_PZVAL_COPY(ret, retval_ptr);    //  Ł™ͿʍǓǔŔ 
                *EG(return_value_ptr_ptr) = ret;    
            }
        }
    
        return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);   //  Ǔ
    ǔĉˆșʒ
    }
    

      函数的重回值在程序执行时存款和储蓄在*EG(return_value_ptr_ptr卡塔尔(قطر‎。ZEND内查对值重临和援用重返作了分别,何况在这里根底上对常量,有的时候变量和此外类型的变量在回来时作了区别的拍卖。在return推行完以往,ZEND内核通过调用zend_leave_helper_SPEC函数,消弭函数内部采用的变量等。这也是ZEND内核自动给函数加上NULL重临的缘故之一。

    本文由威尼斯手机平台登陆发布于最新留言,转载请注明出处:函数的回到值类型应该是规定不改变的,  顾客函数的定义从function 关键字初叶

    关键词: