原:PHP內核研究 函數的返回值


聲明:本文爲斯人原創,全部爲作者一一分析得之,有不對的地方望賜教。
博客地址: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內核研究 函數的返回值

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章