Skip to content

Commit bba615c

Browse files
committed
Implement short foreach
1 parent c5a5391 commit bba615c

4 files changed

Lines changed: 112 additions & 88 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
array test
3+
--FILE--
4+
<?php
5+
6+
foreach([1,2,3,4]) {
7+
echo 'a';
8+
}
9+
10+
11+
foreach([]) {
12+
echo 'a';
13+
}
14+
15+
$range = range(1,5);
16+
17+
foreach($range) {
18+
echo 'c';
19+
}
20+
21+
const RANGE = [1, 2, 3];
22+
23+
foreach(RANGE) {
24+
echo 'c';
25+
}
26+
27+
28+
29+
?>
30+
--EXPECT--
31+
aaaacccccccc
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
array test
3+
--FILE--
4+
<?php
5+
6+
function generator() {
7+
yield from [0,1,2,3];
8+
}
9+
10+
foreach(generator()) {
11+
echo 'a';
12+
}
13+
14+
function generator2() {
15+
for($i=0;$i<0;$i++) {
16+
yield $i;
17+
}
18+
}
19+
20+
foreach(generator2()) {
21+
echo 'b';
22+
}
23+
24+
25+
?>
26+
--EXPECT--
27+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
array test
3+
--FILE--
4+
<?php
5+
class ArrayIteratorEx extends ArrayIterator
6+
{
7+
function current(): mixed
8+
{
9+
return ArrayIterator::current();
10+
}
11+
}
12+
$it = new ArrayIteratorEx(range(0,3));
13+
14+
foreach(new IteratorIterator($it) )
15+
{
16+
echo 'a';
17+
}
18+
19+
20+
21+
?>
22+
--EXPECT--
23+
24+

Zend/zend_compile.c

Lines changed: 30 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -5677,78 +5677,27 @@ static void zend_compile_foreach(zend_ast *ast) /* {{{ */
56775677
zend_ast *value_ast = ast->child[1];
56785678
zend_ast *key_ast = ast->child[2];
56795679
zend_ast *stmt_ast = ast->child[3];
5680+
znode expr_node, reset_node, value_node, key_node;
5681+
zend_op *opline;
5682+
uint32_t opnum_reset, opnum_fetch;
56805683

5681-
if(!value_ast && !key_ast) {
5682-
bool by_ref = value_ast && value_ast->kind == ZEND_AST_REF;
5683-
bool is_variable = zend_is_variable(expr_ast) && zend_can_write_to_variable(expr_ast);
5684-
5685-
znode expr_node, reset_node, value_node, key_node;
5686-
zend_op * opline;
5687-
uint32_t opnum_reset, opnum_fetch;
5688-
5689-
if (key_ast) {
5690-
if (key_ast->kind == ZEND_AST_REF) {
5691-
zend_error_noreturn(E_COMPILE_ERROR, "Key element cannot be a reference");
5692-
}
5693-
if (key_ast->kind == ZEND_AST_ARRAY) {
5694-
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use list as key element");
5695-
}
5696-
}
5697-
5698-
if (by_ref) {
5699-
value_ast = value_ast->child[0];
5700-
}
5701-
5702-
if (value_ast && value_ast->kind == ZEND_AST_ARRAY && zend_propagate_list_refs(value_ast)) {
5703-
by_ref = 1;
5704-
}
5705-
5706-
if (by_ref && is_variable) {
5707-
zend_compile_var(&expr_node, expr_ast, BP_VAR_W, 1);
5708-
} else {
5709-
zend_compile_expr(&expr_node, expr_ast);
5710-
}
5711-
5712-
if (by_ref) {
5713-
zend_separate_if_call_and_write(&expr_node, expr_ast, BP_VAR_W);
5714-
}
5684+
if(!value_ast) {
57155685

5686+
zend_compile_expr(&expr_node, expr_ast);
57165687
opnum_reset = get_next_op_number();
5717-
opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL);
5688+
opline = zend_emit_op(&reset_node, ZEND_FE_RESET_R, &expr_node, NULL);
57185689

57195690
zend_begin_loop(ZEND_FE_FREE, &reset_node, 0);
57205691

57215692
opnum_fetch = get_next_op_number();
5722-
opline = zend_emit_op(NULL, by_ref ? ZEND_FE_FETCH_RW : ZEND_FE_FETCH_R, &reset_node, NULL);
5723-
5724-
if (value_ast && is_this_fetch(value_ast)) {
5725-
zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this");
5726-
} else if (value_ast && value_ast->kind == ZEND_AST_VAR && zend_try_compile_cv(&value_node, value_ast) == SUCCESS) {
5727-
SET_NODE(opline->op2, &value_node);
5728-
} else {
5729-
opline->op2_type = IS_VAR;
5730-
opline->op2.var = get_temporary_variable();
5731-
GET_NODE(&value_node, opline->op2);
5732-
if (value_ast->kind == ZEND_AST_ARRAY) {
5733-
zend_compile_list_assign(NULL, value_ast, &value_node, value_ast->attr);
5734-
} else if (by_ref) {
5735-
zend_emit_assign_ref_znode(value_ast, &value_node);
5736-
} else {
5737-
zend_emit_assign_znode(value_ast, &value_node);
5738-
}
5739-
}
5693+
opline = zend_emit_op(NULL, ZEND_FE_FETCH_R, &reset_node, NULL);
57405694

5741-
if (key_ast) {
5742-
opline = &CG(active_op_array)->opcodes[opnum_fetch];
5743-
zend_make_tmp_result(&key_node, opline);
5744-
zend_emit_assign_znode(key_ast, &key_node);
5745-
}
5695+
opline->op2_type = IS_VAR;
5696+
opline->op2.var = get_temporary_variable();
5697+
GET_NODE(&value_node, opline->op2);
57465698

57475699
zend_compile_stmt(stmt_ast);
57485700

5749-
/* Place JMP and FE_FREE on the line where foreach starts. It would be
5750-
* better to use the end line, but this information is not available
5751-
* currently. */
57525701
CG(zend_lineno) = ast->lineno;
57535702
zend_emit_jump(opnum_fetch);
57545703

@@ -5761,14 +5710,11 @@ static void zend_compile_foreach(zend_ast *ast) /* {{{ */
57615710
zend_end_loop(opnum_fetch, &reset_node);
57625711

57635712
opline = zend_emit_op(NULL, ZEND_FE_FREE, &reset_node, NULL);
5713+
57645714
} else {
5765-
bool by_ref = value_ast && value_ast->kind == ZEND_AST_REF;
5715+
bool by_ref = value_ast->kind == ZEND_AST_REF;
57665716
bool is_variable = zend_is_variable(expr_ast) && zend_can_write_to_variable(expr_ast);
57675717

5768-
znode expr_node, reset_node, value_node, key_node;
5769-
zend_op * opline;
5770-
uint32_t opnum_reset, opnum_fetch;
5771-
57725718
if (key_ast) {
57735719
if (key_ast->kind == ZEND_AST_REF) {
57745720
zend_error_noreturn(E_COMPILE_ERROR, "Key element cannot be a reference");
@@ -5782,7 +5728,7 @@ static void zend_compile_foreach(zend_ast *ast) /* {{{ */
57825728
value_ast = value_ast->child[0];
57835729
}
57845730

5785-
if (value_ast && value_ast->kind == ZEND_AST_ARRAY && zend_propagate_list_refs(value_ast)) {
5731+
if (value_ast->kind == ZEND_AST_ARRAY && zend_propagate_list_refs(value_ast)) {
57865732
by_ref = 1;
57875733
}
57885734

@@ -5797,31 +5743,27 @@ static void zend_compile_foreach(zend_ast *ast) /* {{{ */
57975743
}
57985744

57995745
opnum_reset = get_next_op_number();
5800-
opline = zend_emit_op(&reset_node, ZEND_FE_RESET_R, &expr_node, NULL);
5746+
opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL);
58015747

58025748
zend_begin_loop(ZEND_FE_FREE, &reset_node, 0);
58035749

58045750
opnum_fetch = get_next_op_number();
5805-
opline = zend_emit_op(NULL, ZEND_FE_FETCH_R, &reset_node, NULL);
5806-
if (value_ast) {
5807-
if (is_this_fetch(value_ast)) {
5808-
zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this");
5809-
} else
5810-
if (value_ast->kind == ZEND_AST_VAR && zend_try_compile_cv(&value_node, value_ast) == SUCCESS) {
5811-
SET_NODE(opline->op2, &value_node);
5812-
}
5813-
else {
5814-
opline->op2_type = IS_VAR;
5815-
opline->op2.var = get_temporary_variable();
5816-
GET_NODE(&value_node, opline->op2);
5817-
if (value_ast->kind == ZEND_AST_ARRAY) {
5818-
zend_compile_list_assign(NULL, value_ast, &value_node, value_ast->attr);
5819-
} else
5820-
if (by_ref) {
5821-
zend_emit_assign_ref_znode(value_ast, &value_node);
5822-
} else {
5823-
zend_emit_assign_znode(value_ast, &value_node);
5824-
}
5751+
opline = zend_emit_op(NULL, by_ref ? ZEND_FE_FETCH_RW : ZEND_FE_FETCH_R, &reset_node, NULL);
5752+
if (is_this_fetch(value_ast)) {
5753+
zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this");
5754+
} else if (value_ast->kind == ZEND_AST_VAR &&
5755+
zend_try_compile_cv(&value_node, value_ast) == SUCCESS) {
5756+
SET_NODE(opline->op2, &value_node);
5757+
} else {
5758+
opline->op2_type = IS_VAR;
5759+
opline->op2.var = get_temporary_variable();
5760+
GET_NODE(&value_node, opline->op2);
5761+
if (value_ast->kind == ZEND_AST_ARRAY) {
5762+
zend_compile_list_assign(NULL, value_ast, &value_node, value_ast->attr);
5763+
} else if (by_ref) {
5764+
zend_emit_assign_ref_znode(value_ast, &value_node);
5765+
} else {
5766+
zend_emit_assign_znode(value_ast, &value_node);
58255767
}
58265768
}
58275769

0 commit comments

Comments
 (0)