Skip to content

Commit 2460728

Browse files
committed
Fix nullsafe operator on delayed oplines
1 parent bfeb2f6 commit 2460728

File tree

6 files changed

+117
-5
lines changed

6 files changed

+117
-5
lines changed

Zend/tests/nullsafe_operator/034.phpt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Test nullsafe operator on delayed dim
3+
--FILE--
4+
<?php
5+
6+
$arr = [
7+
'foo' => null,
8+
'bar' => [
9+
'baz' => null,
10+
],
11+
];
12+
13+
var_dump($arr['foo']?->something);
14+
var_dump($arr['invalid']?->something);
15+
16+
var_dump($arr['bar']['baz']?->something);
17+
var_dump($arr['bar']['invalid']?->something);
18+
19+
?>
20+
--EXPECTF--
21+
NULL
22+
23+
Warning: Undefined array key "invalid" in %s.php on line 11
24+
NULL
25+
NULL
26+
27+
Warning: Undefined array key "invalid" in %s.php on line 14
28+
NULL

Zend/tests/nullsafe_operator/035.phpt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
Test nullsafe operator on delayed var
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public ?Bar $bar;
8+
}
9+
10+
class Bar {
11+
public string $baz;
12+
}
13+
14+
$foo = new Foo();
15+
16+
$foo->bar = null;
17+
var_dump($foo->bar?->baz);
18+
19+
$bar = new Bar();
20+
$bar->baz = 'baz';
21+
$foo->bar = $bar;
22+
var_dump($foo->bar?->baz);
23+
24+
?>
25+
--EXPECT--
26+
NULL
27+
string(3) "baz"

Zend/tests/nullsafe_operator/036.phpt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Test nullsafe method call on delayed var
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public ?Bar $bar;
8+
}
9+
10+
class Bar {
11+
public function baz() {
12+
return 'baz';
13+
}
14+
}
15+
16+
$foo = new Foo();
17+
18+
$foo->bar = null;
19+
var_dump($foo->bar?->baz());
20+
21+
$bar = new Bar();
22+
$foo->bar = $bar;
23+
var_dump($foo->bar?->baz());
24+
25+
?>
26+
--EXPECT--
27+
NULL
28+
string(3) "baz"

Zend/tests/nullsafe_operator/037.phpt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
Test nullsafe operator in netsted delayed dims
3+
--FILE--
4+
<?php
5+
6+
$foo = new stdClass();
7+
$foo->bar = 'bar';
8+
9+
$array = ['foo' => ['bar' => 'baz']];
10+
11+
var_dump($array['foo'][$foo?->bar]);
12+
13+
?>
14+
--EXPECT--
15+
string(3) "baz"

Zend/zend_compile.c

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ void zend_init_compiler_data_structures(void) /* {{{ */
372372
CG(active_class_entry) = NULL;
373373
CG(in_compilation) = 0;
374374
CG(skip_shebang) = 0;
375+
CG(last_delayed_oplines_offset) = 0;
375376

376377
CG(encoding_declared) = 0;
377378
CG(memoized_exprs) = NULL;
@@ -2208,7 +2209,9 @@ static inline zend_op *zend_delayed_emit_op(znode *result, zend_uchar opcode, zn
22082209

22092210
static inline uint32_t zend_delayed_compile_begin(void) /* {{{ */
22102211
{
2211-
return zend_stack_count(&CG(delayed_oplines_stack));
2212+
uint32_t offset = zend_stack_count(&CG(delayed_oplines_stack));
2213+
CG(last_delayed_oplines_offset) = offset;
2214+
return offset;
22122215
}
22132216
/* }}} */
22142217

@@ -2223,6 +2226,7 @@ static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */
22232226
memcpy(opline, &oplines[i], sizeof(zend_op));
22242227
}
22252228
CG(delayed_oplines_stack).top = offset;
2229+
CG(last_delayed_oplines_offset) = offset;
22262230
return opline;
22272231
}
22282232
/* }}} */
@@ -2305,10 +2309,19 @@ static void zend_short_circuiting_commit(uint32_t checkpoint, znode *result, zen
23052309
}
23062310
}
23072311

2308-
static void zend_emit_jmp_null(znode *obj_node)
2312+
static void zend_emit_jmp_null(znode *obj_node, bool delay)
23092313
{
23102314
uint32_t jmp_null_opnum = get_next_op_number();
2311-
zend_op *opline = zend_emit_op(NULL, ZEND_JMP_NULL, obj_node, NULL);
2315+
zend_op *opline;
2316+
2317+
if (delay) {
2318+
uint32_t delayed_oplines_count = zend_stack_count(&CG(delayed_oplines_stack));
2319+
jmp_null_opnum += (delayed_oplines_count - CG(last_delayed_oplines_offset));
2320+
opline = zend_delayed_emit_op(NULL, ZEND_JMP_NULL, obj_node, NULL);
2321+
} else {
2322+
opline = zend_emit_op(NULL, ZEND_JMP_NULL, obj_node, NULL);
2323+
}
2324+
23122325
if (opline->op1_type == IS_CONST) {
23132326
Z_TRY_ADDREF_P(CT_CONSTANT(opline->op1));
23142327
}
@@ -2796,7 +2809,7 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
27962809
opline = zend_delayed_compile_var(&obj_node, obj_ast, type, 0);
27972810
zend_separate_if_call_and_write(&obj_node, obj_ast, type);
27982811
if (nullsafe) {
2799-
zend_emit_jmp_null(&obj_node);
2812+
zend_emit_jmp_null(&obj_node, 1);
28002813
}
28012814
}
28022815

@@ -4356,7 +4369,7 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{
43564369
zend_short_circuiting_mark_inner(obj_ast);
43574370
zend_compile_expr(&obj_node, obj_ast);
43584371
if (nullsafe) {
4359-
zend_emit_jmp_null(&obj_node);
4372+
zend_emit_jmp_null(&obj_node, 0);
43604373
}
43614374
}
43624375

Zend/zend_globals.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ struct _zend_compiler_globals {
119119
zend_arena *ast_arena;
120120

121121
zend_stack delayed_oplines_stack;
122+
uint32_t last_delayed_oplines_offset;
122123
HashTable *memoized_exprs;
123124
int memoize_mode;
124125

0 commit comments

Comments
 (0)