上一节讲到 函数的参数 下面继续分析函数的返回值.. 从根本来说,PHP的每个函数或方法都存在返回值,可能有的时候不写return, 这个时候 会返回NULL.
function test(){ return 1; }
经过分析找到token
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); }
上面三个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
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); }
根据操作数的不同,ZEND_RETURN中间代码会执行 ZEND_RETURN_SPEC_CONST_HANDLER, ZEND_RETURN_SPEC_TMP_HANDLER或ZEND_RETURN_SPEC_TMP_HANDLER.这几个代码流程基本相同 以 ZEND_RETURN_SPEC_CONST_HANDLER为例.
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); }
函数返回值 存储在EG(return_value_ptr_ptr); 当然 PHP还有很多 用于函数返回的内置宏如 RETURN_BOOL RETURN_NULL RETURN_RESOURCE RETURN_LONG RETURN_DOUBLE 等等…