Skip to content

Commit da04bf6

Browse files
committed
Forbid callable syntax + nullsafe
1 parent 297271f commit da04bf6

File tree

4 files changed

+61
-22
lines changed

4 files changed

+61
-22
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
First class callable with nullsafe method call
3+
--FILE--
4+
<?php
5+
6+
$foo?->bar(...);
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Cannot combine nullsafe operator with Closure creation in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
First class callable with nullsafe method call (nested)
3+
--FILE--
4+
<?php
5+
6+
$foo?->foo->bar(...);
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Cannot combine nullsafe operator with Closure creation in %s on line %d
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
First class callable with nullsafe method call (unrelated)
3+
--FILE--
4+
<?php
5+
6+
$foo = null;
7+
var_dump($foo?->foo($foo->bar(...)));
8+
9+
?>
10+
--EXPECT--
11+
NULL

Zend/zend_compile.c

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3674,30 +3674,12 @@ ZEND_API zend_uchar zend_get_call_op(const zend_op *init_op, zend_function *fbc)
36743674
}
36753675
/* }}} */
36763676

3677-
void zend_compile_call_common(znode *result, zend_ast *args_ast, zend_function *fbc) /* {{{ */
3677+
bool zend_compile_call_common(znode *result, zend_ast *args_ast, zend_function *fbc) /* {{{ */
36783678
{
36793679
zend_op *opline;
36803680
uint32_t opnum_init = get_next_op_number() - 1;
36813681

3682-
if (args_ast->kind != ZEND_AST_CALLABLE_CONVERT) {
3683-
bool may_have_extra_named_args;
3684-
uint32_t arg_count = zend_compile_args(args_ast, fbc, &may_have_extra_named_args);
3685-
3686-
zend_do_extended_fcall_begin();
3687-
3688-
opline = &CG(active_op_array)->opcodes[opnum_init];
3689-
opline->extended_value = arg_count;
3690-
3691-
if (opline->opcode == ZEND_INIT_FCALL) {
3692-
opline->op1.num = zend_vm_calc_used_stack(arg_count, fbc);
3693-
}
3694-
3695-
opline = zend_emit_op(result, zend_get_call_op(opline, fbc), NULL, NULL);
3696-
if (may_have_extra_named_args) {
3697-
opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS;
3698-
}
3699-
zend_do_extended_fcall_end();
3700-
} else {
3682+
if (args_ast->kind == ZEND_AST_CALLABLE_CONVERT) {
37013683
opline = &CG(active_op_array)->opcodes[opnum_init];
37023684
opline->extended_value = 0;
37033685

@@ -3709,8 +3691,28 @@ void zend_compile_call_common(znode *result, zend_ast *args_ast, zend_function *
37093691
opline->op1.num = zend_vm_calc_used_stack(0, fbc);
37103692
}
37113693

3712-
opline = zend_emit_op(result, ZEND_CALLABLE_CONVERT, NULL, NULL);
3694+
zend_emit_op(result, ZEND_CALLABLE_CONVERT, NULL, NULL);
3695+
return true;
37133696
}
3697+
3698+
bool may_have_extra_named_args;
3699+
uint32_t arg_count = zend_compile_args(args_ast, fbc, &may_have_extra_named_args);
3700+
3701+
zend_do_extended_fcall_begin();
3702+
3703+
opline = &CG(active_op_array)->opcodes[opnum_init];
3704+
opline->extended_value = arg_count;
3705+
3706+
if (opline->opcode == ZEND_INIT_FCALL) {
3707+
opline->op1.num = zend_vm_calc_used_stack(arg_count, fbc);
3708+
}
3709+
3710+
opline = zend_emit_op(result, zend_get_call_op(opline, fbc), NULL, NULL);
3711+
if (may_have_extra_named_args) {
3712+
opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS;
3713+
}
3714+
zend_do_extended_fcall_end();
3715+
return false;
37143716
}
37153717
/* }}} */
37163718

@@ -4499,6 +4501,7 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{
44994501
zend_op *opline;
45004502
zend_function *fbc = NULL;
45014503
bool nullsafe = ast->kind == ZEND_AST_NULLSAFE_METHOD_CALL;
4504+
uint32_t short_circuiting_checkpoint = zend_short_circuiting_checkpoint();
45024505

45034506
if (is_this_fetch(obj_ast)) {
45044507
if (this_guaranteed_exists()) {
@@ -4547,7 +4550,12 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{
45474550
}
45484551
}
45494552

4550-
zend_compile_call_common(result, args_ast, fbc);
4553+
if (zend_compile_call_common(result, args_ast, fbc)) {
4554+
if (short_circuiting_checkpoint != zend_short_circuiting_checkpoint()) {
4555+
zend_error_noreturn(E_COMPILE_ERROR,
4556+
"Cannot combine nullsafe operator with Closure creation");
4557+
}
4558+
}
45514559
}
45524560
/* }}} */
45534561

0 commit comments

Comments
 (0)