Skip to content

Commit 04e3523

Browse files
committed
Warn if continue is used on switch
Supersedes RFC https://wiki.php.net/rfc/continue_on_switch_deprecation by generating a warning instead of deprecating and removing this functionality.
1 parent 41a6625 commit 04e3523

File tree

4 files changed

+94
-6
lines changed

4 files changed

+94
-6
lines changed

UPGRADING

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,19 @@ Core:
4444
the following "FOO;" will cause a syntax error. This issue can always be
4545
resolved by choosing an ending label that does not occur within the contents
4646
of the string.
47+
. "continue" statements targeting "switch" control flow structures will now
48+
generate a warning. In PHP such "continue" statements are equivalent to
49+
"break", while they behave as "continue 2" in other languages.
50+
51+
while ($foo) {
52+
switch ($bar) {
53+
case "baz":
54+
continue;
55+
// Warning: "continue" targeting switch is equivalent to
56+
"break". Did you mean to use "continue 2"?
57+
}
58+
}
59+
4760
. Array accesses of type $obj["123"], where $obj implements ArrayAccess and
4861
"123" is an integral string literal will no longer result in an implicit
4962
conversion to integer, i.e., $obj->offsetGet("123") will be called instead
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
--TEST--
2+
Warning on continue targeting switch
3+
--FILE--
4+
<?php
5+
6+
function test() {
7+
switch ($foo) {
8+
case 0:
9+
continue; // INVALID
10+
case 1:
11+
break;
12+
}
13+
14+
while ($foo) {
15+
switch ($bar) {
16+
case 0:
17+
continue; // INVALID
18+
case 1:
19+
continue 2;
20+
case 2:
21+
break;
22+
}
23+
}
24+
25+
while ($foo) {
26+
switch ($bar) {
27+
case 0:
28+
while ($xyz) {
29+
continue 2; // INVALID
30+
}
31+
case 1:
32+
while ($xyz) {
33+
continue 3;
34+
}
35+
case 2:
36+
while ($xyz) {
37+
break 2;
38+
}
39+
}
40+
}
41+
}
42+
43+
?>
44+
--EXPECTF--
45+
Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in %s on line 6
46+
47+
Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in %s on line 14
48+
49+
Warning: "continue 2" targeting switch is equivalent to "break 2". Did you mean to use "continue 3"? in %s on line 26

Zend/zend_compile.c

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,8 @@ static void zend_end_live_range(zend_op_array *op_array, uint32_t offset, uint32
649649
}
650650
/* }}} */
651651

652-
static inline void zend_begin_loop(zend_uchar free_opcode, const znode *loop_var) /* {{{ */
652+
static inline void zend_begin_loop(
653+
zend_uchar free_opcode, const znode *loop_var, zend_bool is_switch) /* {{{ */
653654
{
654655
zend_brk_cont_element *brk_cont_element;
655656
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
658659
CG(context).current_brk_cont = CG(context).last_brk_cont;
659660
brk_cont_element = get_next_brk_cont_element();
660661
brk_cont_element->parent = parent;
662+
brk_cont_element->is_switch = is_switch;
661663

662664
if (loop_var && (loop_var->op_type & (IS_VAR|IS_TMP_VAR))) {
663665
uint32_t start = get_next_op_number(CG(active_op_array));
@@ -4585,6 +4587,29 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */
45854587
depth, depth == 1 ? "" : "s");
45864588
}
45874589
}
4590+
4591+
if (ast->kind == ZEND_AST_CONTINUE) {
4592+
int d, cur = CG(context).current_brk_cont;
4593+
for (d = depth - 1; d > 0; d--) {
4594+
cur = CG(context).brk_cont_array[cur].parent;
4595+
ZEND_ASSERT(cur != -1);
4596+
}
4597+
4598+
if (CG(context).brk_cont_array[cur].is_switch) {
4599+
if (depth == 1) {
4600+
zend_error(E_WARNING,
4601+
"\"continue\" targeting switch is equivalent to \"break\". " \
4602+
"Did you mean to use \"continue %d\"?",
4603+
depth + 1);
4604+
} else {
4605+
zend_error(E_WARNING,
4606+
"\"continue %d\" targeting switch is equivalent to \"break %d\". " \
4607+
"Did you mean to use \"continue %d\"?",
4608+
depth, depth, depth + 1);
4609+
}
4610+
}
4611+
}
4612+
45884613
opline = zend_emit_op(NULL, ast->kind == ZEND_AST_BREAK ? ZEND_BRK : ZEND_CONT, NULL, NULL);
45894614
opline->op1.num = CG(context).current_brk_cont;
45904615
opline->op2.num = depth;
@@ -4697,7 +4722,7 @@ void zend_compile_while(zend_ast *ast) /* {{{ */
46974722

46984723
opnum_jmp = zend_emit_jump(0);
46994724

4700-
zend_begin_loop(ZEND_NOP, NULL);
4725+
zend_begin_loop(ZEND_NOP, NULL, 0);
47014726

47024727
opnum_start = get_next_op_number(CG(active_op_array));
47034728
zend_compile_stmt(stmt_ast);
@@ -4720,7 +4745,7 @@ void zend_compile_do_while(zend_ast *ast) /* {{{ */
47204745
znode cond_node;
47214746
uint32_t opnum_start, opnum_cond;
47224747

4723-
zend_begin_loop(ZEND_NOP, NULL);
4748+
zend_begin_loop(ZEND_NOP, NULL, 0);
47244749

47254750
opnum_start = get_next_op_number(CG(active_op_array));
47264751
zend_compile_stmt(stmt_ast);
@@ -4771,7 +4796,7 @@ void zend_compile_for(zend_ast *ast) /* {{{ */
47714796

47724797
opnum_jmp = zend_emit_jump(0);
47734798

4774-
zend_begin_loop(ZEND_NOP, NULL);
4799+
zend_begin_loop(ZEND_NOP, NULL, 0);
47754800

47764801
opnum_start = get_next_op_number(CG(active_op_array));
47774802
zend_compile_stmt(stmt_ast);
@@ -4834,7 +4859,7 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */
48344859
opnum_reset = get_next_op_number(CG(active_op_array));
48354860
opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL);
48364861

4837-
zend_begin_loop(ZEND_FE_FREE, &reset_node);
4862+
zend_begin_loop(ZEND_FE_FREE, &reset_node, 0);
48384863

48394864
opnum_fetch = get_next_op_number(CG(active_op_array));
48404865
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) /* {{{ */
49895014

49905015
zend_compile_expr(&expr_node, expr_ast);
49915016

4992-
zend_begin_loop(ZEND_FREE, &expr_node);
5017+
zend_begin_loop(ZEND_FREE, &expr_node, 1);
49935018

49945019
case_node.op_type = IS_TMP_VAR;
49955020
case_node.u.op.var = get_temporary_variable(CG(active_op_array));

Zend/zend_compile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ typedef struct _zend_brk_cont_element {
158158
int cont;
159159
int brk;
160160
int parent;
161+
zend_bool is_switch;
161162
} zend_brk_cont_element;
162163

163164
typedef struct _zend_label {

0 commit comments

Comments
 (0)