设为首页 收藏本站
查看: 547|回复: 0

[经验分享] 原:PHP内核研究 函数的返回值

[复制链接]

尚未签到

发表于 2017-4-9 08:31:35 | 显示全部楼层 |阅读模式









声明:本文为斯人原创,全部为作者一一分析得之,有不对的地方望赐教。

博客地址:PHP技术博客在CSDN也会同步更新的哦.

欢迎转载,转载请注明出处

上一节讲到 函数的参数

下面继续分析函数的返回值..

从根本来说,PHP的每个函数或方法都存在返回值,可能有的时候不写return,

这个时候 会返回NULL.

[php]
function test(){
         return 1;
}
[/php]



经过分析找到token


[c]
T_RETURN ';'                                            { zend_do_return(NULL, 0 TSRM     LS_CC); }
         |       T_RETURN expr_without_variable ';'      { zend_do_return(&$2, 0 TSRMLS_CC); }
         |       T_RETURN variable ';'                           { zend_do_return(&$2, 1 TSRMLS_CC); }
[/c]



上面三个token代表函数返回的三种形式,

return ;

return 立即数(字符串);

return 变量; //返回变量/引用返回

执行的函数为 zend_do_return

PHP对三种不同的返回值做了不同的处理.我们详细来看一下

zend_do_return(NULL, 0 TSRM LS_CC);是返回NULL

zend_do_return(&$2, 0 TSRMLS_CC); //直接返回 立即数或字符串

zend_do_return(&$2, 1 TSRMLS_CC); //返回变量/引用返回

定义如下Zend/zend_compile.c


[c]
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);//普通变量返回
                }
        }
        //当前op位置
        start_op_number = get_next_op_number(CG(active_op_array));

#ifdef ZTS
        zend_stack_apply(&CG(foreach_copy_stack), ZEND_STACK_APPLY_TOPDOWN, (int (*)(void *element)) generate_free_foreach_copy);
#endif

        end_op_number = get_next_op_number(CG(active_op_array));
        while (start_op_number < end_op_number) {
                CG(active_op_array)->opcodes[start_op_number].op1.u.EA.type = EXT_TYPE_FREE_ON_RETURN;
                start_op_number++;
        }

        opline = get_next_op(CG(active_op_array) 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);
}
[/c]



根据操作数的不同,ZEND_RETURN中间代码会执行 ZEND_RETURN_SPEC_CONST_HANDLER, ZEND_RETURN_SPEC_TMP_HANDLER或ZEND_RETURN_SPEC_TMP_HANDLER.这几个代码流程基本相同

以 ZEND_RETURN_SPEC_CONST_HANDLER为例.


[c]
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) {//引用返回

                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;//返回值初始化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) {
                                if (IS_CONST == IS_VAR && !0) {
                                }
                                goto return_by_value;
                        }
                }

                if (EG(return_value_ptr_ptr)) {
                        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 if ((IS_CONST == IS_CV || IS_CONST == IS_VAR) &&
                                   retval_ptr == &EG(uninitialized_zval)) {
                                zval *ret;

                                ALLOC_INIT_ZVAL(ret);
                                *EG(return_value_ptr_ptr) = ret;
                        } else {
                                *EG(return_value_ptr_ptr) = retval_ptr;
                                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);
}
[/c]



函数返回值 存储在EG(return_value_ptr_ptr);

当然 PHP还有很多 用于函数返回的内置宏如

RETURN_BOOL

RETURN_NULL

RETURN_RESOURCE

RETURN_LONG

RETURN_DOUBLE

等等...

原文出处:
原:PHP内核研究
函数的返回值

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-362220-1-1.html 上篇帖子: iPhone 搭建PHP版Push服务器 实例操作 下篇帖子: 一个好用的功能强大的php分页类
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表