Skip to content

Commit d0f6dfe

Browse files
committed
Merge branch 'PHP-8.6'
* PHP-8.6: Fix GH-23979: Nullsafe operator must not flush delayed oplines of an enclosing function
2 parents ccd06d4 + b4f14f4 commit d0f6dfe

2 files changed

Lines changed: 26 additions & 3 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
GH-23979 (Nullsafe operator must not flush delayed oplines of an enclosing function)
3+
--FILE--
4+
<?php
5+
function test($name) {
6+
$arr = ['foo' => 'bar'];
7+
return ${$name}[(function () {
8+
return A . B?->prop;
9+
})()];
10+
}
11+
const A = 'foo';
12+
const B = null;
13+
var_dump(test('arr'));
14+
?>
15+
--EXPECT--
16+
string(3) "bar"

‎Zend/zend_compile.c‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2489,10 +2489,15 @@ static inline zend_op *zend_delayed_emit_op(znode *result, uint8_t opcode, znode
24892489
}
24902490
/* }}} */
24912491

2492-
static inline uint32_t zend_delayed_compile_begin(void) /* {{{ */
2492+
static zend_always_inline uint32_t zend_delayed_oplines_stack_size(void)
24932493
{
24942494
return zend_stack_count(&CG(delayed_oplines_stack));
24952495
}
2496+
2497+
static inline uint32_t zend_delayed_compile_begin(void) /* {{{ */
2498+
{
2499+
return zend_delayed_oplines_stack_size();
2500+
}
24962501
/* }}} */
24972502

24982503
static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */
@@ -3212,6 +3217,7 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
32123217
/* We will throw if $this doesn't exist, so there's no need to emit a JMP_NULL
32133218
* check for a nullsafe access. */
32143219
} else {
3220+
uint32_t offset = zend_delayed_oplines_stack_size();
32153221
zend_short_circuiting_mark_inner(obj_ast);
32163222
zend_mark_const_object_fetch(obj_ast);
32173223
opline = zend_delayed_compile_var(&obj_node, obj_ast, type, false);
@@ -3228,10 +3234,11 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
32283234
/* Flush delayed oplines */
32293235
zend_op *opline = NULL, *oplines = zend_stack_base(&CG(delayed_oplines_stack));
32303236
uint32_t var = obj_node.u.op.var;
3231-
uint32_t count = zend_stack_count(&CG(delayed_oplines_stack));
3237+
uint32_t count = zend_delayed_oplines_stack_size();
32323238
uint32_t i = count;
32333239

3234-
while (i > 0 && oplines[i-1].result_type == IS_TMP_VAR && oplines[i-1].result.var == var) {
3240+
/* Only consider the oplines delayed while compiling obj_ast. */
3241+
while (i > offset && oplines[i-1].result_type == IS_TMP_VAR && oplines[i-1].result.var == var) {
32353242
i--;
32363243
if (oplines[i].op1_type == IS_TMP_VAR) {
32373244
var = oplines[i].op1.var;

0 commit comments

Comments
 (0)