原:PHP内核源码分析之foreach 二
浏览数:713 时间:2013-12-06
如果您没有看第一节,请务必先看 PHP内核源码分析之foreach 一
foreach的变量可以是引用,也可以是普通变量..
例如
一. foreach ($arr as $v){ } 二. foreach($arr as &$v){ }
继续看内核源码
{ zend_do_foreach_begin(&$1, &$2, &$3, &$4, 1 TSRMLS_CC); } foreach_variable foreach_optional_arg ')' { zend_do_foreach_cont(&$1, &$2, &$4, &$6, &$7 TSRMLS_CC); }
为了实现这个功能,
PHP 的 foreach_variable 定义了两个规则
foreach_variable: variable { zend_check_writable_variable(&$1); $$ = $1; } | '&' variable { zend_check_writable_variable(&$2); $$ = $2; $$.u.EA.type |= ZEND_PARSED_REFERENCE_VARIABLE; } ;
多了一个 $$.u.EA.type |= ZEND_PARSED_REFERENCE_VARIABLE;
意思为 以引用的方式返回该值.
zend_check_writable_variable 用来检测 变量是否是函数或者某个对象的方法,
再来看看zend_do_foreach_cont
void zend_do_foreach_cont(znode *foreach_token, const znode *open_brackets_token, const znode *as_token, znode *value, znode *key TSRMLS_DC) /* {{{ */ { zend_op *opline; znode dummy, value_node; zend_bool assign_by_ref=0; opline = &CG(active_op_array)->opcodes[as_token->u.opline_num]; if (key->op_type != IS_UNUSED) { znode *tmp; /* switch between the key and value... */ tmp = key; key = value; value = tmp; /* Mark extended_value in case both key and value are being used */ opline->extended_value |= ZEND_FE_FETCH_WITH_KEY; } if ((key->op_type != IS_UNUSED) && (key->u.EA.type & ZEND_PARSED_REFERENCE_VARIABLE)) { zend_error(E_COMPILE_ERROR, "Key element cannot be a reference"); } if (value->u.EA.type & ZEND_PARSED_REFERENCE_VARIABLE) { assign_by_ref = 1; if (!(opline-1)->extended_value) { } /* Mark extended_value for assign-by-reference */ opline->extended_value |= ZEND_FE_FETCH_BYREF; CG(active_op_array)->opcodes[foreach_token->u.opline_num].extended_value |= ZEND_FE_RESET_REFERENCE; } else { zend_op *foreach_copy; zend_op *fetch = &CG(active_op_array)->opcodes[foreach_token->u.opline_num]; zend_op *end = &CG(active_op_array)->opcodes[open_brackets_token->u.opline_num]; /* Change "write context" into "read context" */ fetch->extended_value = 0; /* reset ZEND_FE_RESET_VARIABLE */ while (fetch != end) { --fetch; if (fetch->opcode == ZEND_FETCH_DIM_W && fetch->op2.op_type == IS_UNUSED) { zend_error(E_COMPILE_ERROR, "Cannot use [] for reading"); } fetch->opcode -= 3; /* FETCH_W -> FETCH_R */ } /* prevent double SWITCH_FREE */ zend_stack_top(&CG(foreach_copy_stack), (void **) &foreach_copy); foreach_copy->op1.op_type = IS_UNUSED; } value_node = opline->result; if (assign_by_ref) { zend_do_end_variable_parse(value, BP_VAR_W, 0 TSRMLS_CC); /* Mark FE_FETCH as IS_VAR as it holds the data directly as a value */ zend_do_assign_ref(NULL, value, &value_node TSRMLS_CC); } else { zend_do_assign(&dummy, value, &value_node TSRMLS_CC); zend_do_free(&dummy TSRMLS_CC); } if (key->op_type != IS_UNUSED) { znode key_node; opline = &CG(active_op_array)->opcodes[as_token->u.opline_num+1]; opline->result.op_type = IS_TMP_VAR; opline->result.u.EA.type = 0; opline->result.u.opline_num = get_temporary_variable(CG(active_op_array)); key_node = opline->result; zend_do_assign(&dummy, key, &key_node TSRMLS_CC); zend_do_free(&dummy TSRMLS_CC); } do_begin_loop(TSRMLS_C); INC_BPC(CG(active_op_array)); }
8-11行 交换 key和value的值
19-21行 key不能作为引用的方式获取
23-29行 val以引用的方式获取
32行 获取foreach执行的op代码
33行 获取 括号的op代码
51-54 获取引用值
54-58 复制值
60-71 获取key的值
do_begin_loop 初始化 foreach 循环.
void zend_do_foreach_end(const znode *foreach_token, const znode *as_token TSRMLS_DC) /* {{{ */ { zend_op *container_ptr; zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = ZEND_JMP; opline->op1.u.opline_num = as_token->u.opline_num; SET_UNUSED(opline->op1); SET_UNUSED(opline->op2); CG(active_op_array)->opcodes[foreach_token->u.opline_num].op2.u.opline_num = get_next_op_number(CG(active_op_array)); /* FE_RESET */ CG(active_op_array)->opcodes[as_token->u.opline_num].op2.u.opline_num = get_next_op_number(CG(active_op_array)); /* FE_FETCH */ do_end_loop(as_token->u.opline_num, 1 TSRMLS_CC); zend_stack_top(&CG(foreach_copy_stack), (void **) &container_ptr); generate_free_foreach_copy(container_ptr TSRMLS_CC); zend_stack_del_top(&CG(foreach_copy_stack)); DEC_BPC(CG(active_op_array)); }
生成一个跳转的opcode,
设置 op为ZEND_JMP
当前op将要跳转的位置
设置op跳出的位置
结束循环
一些清理操作…
foreach_satement就是 我们foreach的中间代码了 它会解析成 zend_do_extended_info这个函数来处理中间代码
这个函数之前的文章有提到过..这里就不说了
友情链接