diff --git a/UPGRADING b/UPGRADING index dc749ee67c5..98daa08670b 100644 --- a/UPGRADING +++ b/UPGRADING @@ -44,6 +44,19 @@ Core: the following "FOO;" will cause a syntax error. This issue can always be resolved by choosing an ending label that does not occur within the contents of the string. + . "continue" statements targeting "switch" control flow structures will now + generate a warning. In PHP such "continue" statements are equivalent to + "break", while they behave as "continue 2" in other languages. + + while ($foo) { + switch ($bar) { + case "baz": + continue; + // Warning: "continue" targeting switch is equivalent to + "break". Did you mean to use "continue 2"? + } + } + . Array accesses of type $obj["123"], where $obj implements ArrayAccess and "123" is an integral string literal will no longer result in an implicit conversion to integer, i.e., $obj->offsetGet("123") will be called instead diff --git a/Zend/tests/continue_targeting_switch_warning.phpt b/Zend/tests/continue_targeting_switch_warning.phpt new file mode 100644 index 00000000000..67b5159b7d6 --- /dev/null +++ b/Zend/tests/continue_targeting_switch_warning.phpt @@ -0,0 +1,49 @@ +--TEST-- +Warning on continue targeting switch +--FILE-- + +--EXPECTF-- +Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in %s on line 6 + +Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in %s on line 14 + +Warning: "continue 2" targeting switch is equivalent to "break 2". Did you mean to use "continue 3"? in %s on line 26 diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index c050855ae8d..3eece7687cd 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -649,7 +649,8 @@ static void zend_end_live_range(zend_op_array *op_array, uint32_t offset, uint32 } /* }}} */ -static inline void zend_begin_loop(zend_uchar free_opcode, const znode *loop_var) /* {{{ */ +static inline void zend_begin_loop( + zend_uchar free_opcode, const znode *loop_var, zend_bool is_switch) /* {{{ */ { zend_brk_cont_element *brk_cont_element; int parent = CG(context).current_brk_cont; @@ -658,6 +659,7 @@ static inline void zend_begin_loop(zend_uchar free_opcode, const znode *loop_var CG(context).current_brk_cont = CG(context).last_brk_cont; brk_cont_element = get_next_brk_cont_element(); brk_cont_element->parent = parent; + brk_cont_element->is_switch = is_switch; if (loop_var && (loop_var->op_type & (IS_VAR|IS_TMP_VAR))) { uint32_t start = get_next_op_number(CG(active_op_array)); @@ -4585,6 +4587,29 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */ depth, depth == 1 ? "" : "s"); } } + + if (ast->kind == ZEND_AST_CONTINUE) { + int d, cur = CG(context).current_brk_cont; + for (d = depth - 1; d > 0; d--) { + cur = CG(context).brk_cont_array[cur].parent; + ZEND_ASSERT(cur != -1); + } + + if (CG(context).brk_cont_array[cur].is_switch) { + if (depth == 1) { + zend_error(E_WARNING, + "\"continue\" targeting switch is equivalent to \"break\". " \ + "Did you mean to use \"continue %d\"?", + depth + 1); + } else { + zend_error(E_WARNING, + "\"continue %d\" targeting switch is equivalent to \"break %d\". " \ + "Did you mean to use \"continue %d\"?", + depth, depth, depth + 1); + } + } + } + opline = zend_emit_op(NULL, ast->kind == ZEND_AST_BREAK ? ZEND_BRK : ZEND_CONT, NULL, NULL); opline->op1.num = CG(context).current_brk_cont; opline->op2.num = depth; @@ -4697,7 +4722,7 @@ void zend_compile_while(zend_ast *ast) /* {{{ */ opnum_jmp = zend_emit_jump(0); - zend_begin_loop(ZEND_NOP, NULL); + zend_begin_loop(ZEND_NOP, NULL, 0); opnum_start = get_next_op_number(CG(active_op_array)); zend_compile_stmt(stmt_ast); @@ -4720,7 +4745,7 @@ void zend_compile_do_while(zend_ast *ast) /* {{{ */ znode cond_node; uint32_t opnum_start, opnum_cond; - zend_begin_loop(ZEND_NOP, NULL); + zend_begin_loop(ZEND_NOP, NULL, 0); opnum_start = get_next_op_number(CG(active_op_array)); zend_compile_stmt(stmt_ast); @@ -4771,7 +4796,7 @@ void zend_compile_for(zend_ast *ast) /* {{{ */ opnum_jmp = zend_emit_jump(0); - zend_begin_loop(ZEND_NOP, NULL); + zend_begin_loop(ZEND_NOP, NULL, 0); opnum_start = get_next_op_number(CG(active_op_array)); zend_compile_stmt(stmt_ast); @@ -4834,7 +4859,7 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ opnum_reset = get_next_op_number(CG(active_op_array)); opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL); - zend_begin_loop(ZEND_FE_FREE, &reset_node); + zend_begin_loop(ZEND_FE_FREE, &reset_node, 0); opnum_fetch = get_next_op_number(CG(active_op_array)); opline = zend_emit_op(NULL, by_ref ? ZEND_FE_FETCH_RW : ZEND_FE_FETCH_R, &reset_node, NULL); @@ -4989,7 +5014,7 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */ zend_compile_expr(&expr_node, expr_ast); - zend_begin_loop(ZEND_FREE, &expr_node); + zend_begin_loop(ZEND_FREE, &expr_node, 1); case_node.op_type = IS_TMP_VAR; case_node.u.op.var = get_temporary_variable(CG(active_op_array)); diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 9ba1b54f33b..bf24904834b 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -158,6 +158,7 @@ typedef struct _zend_brk_cont_element { int cont; int brk; int parent; + zend_bool is_switch; } zend_brk_cont_element; typedef struct _zend_label {