Skip to content

Commit e95a877

Browse files
committed
Forbid callable syntax + nullsafe
1 parent c6e3ea8 commit e95a877

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
@@ -3669,30 +3669,12 @@ ZEND_API zend_uchar zend_get_call_op(const zend_op *init_op, zend_function *fbc)
36693669
}
36703670
/* }}} */
36713671

3672-
void zend_compile_call_common(znode *result, zend_ast *args_ast, zend_function *fbc) /* {{{ */
3672+
bool zend_compile_call_common(znode *result, zend_ast *args_ast, zend_function *fbc) /* {{{ */
36733673
{
36743674
zend_op *opline;
36753675
uint32_t opnum_init = get_next_op_number() - 1;
36763676

3677-
if (args_ast->kind != ZEND_AST_CALLABLE_CONVERT) {
3678-
bool may_have_extra_named_args;
3679-
uint32_t arg_count = zend_compile_args(args_ast, fbc, &may_have_extra_named_args);
3680-
3681-
zend_do_extended_fcall_begin();
3682-
3683-
opline = &CG(active_op_array)->opcodes[opnum_init];
3684-
opline->extended_value = arg_count;
3685-
3686-
if (opline->opcode == ZEND_INIT_FCALL) {
3687-
opline->op1.num = zend_vm_calc_used_stack(arg_count, fbc);
3688-
}
3689-
3690-
opline = zend_emit_op(result, zend_get_call_op(opline, fbc), NULL, NULL);
3691-
if (may_have_extra_named_args) {
3692-
opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS;
3693-
}
3694-
zend_do_extended_fcall_end();
3695-
} else {
3677+
if (args_ast->kind == ZEND_AST_CALLABLE_CONVERT) {
36963678
opline = &CG(active_op_array)->opcodes[opnum_init];
36973679
opline->extended_value = 0;
36983680

@@ -3704,8 +3686,28 @@ void zend_compile_call_common(znode *result, zend_ast *args_ast, zend_function *
37043686
opline->op1.num = zend_vm_calc_used_stack(0, fbc);
37053687
}
37063688

3707-
opline = zend_emit_op(result, ZEND_CALLABLE_CONVERT, NULL, NULL);
3689+
zend_emit_op(result, ZEND_CALLABLE_CONVERT, NULL, NULL);
3690+
return true;
37083691
}
3692+
3693+
bool may_have_extra_named_args;
3694+
uint32_t arg_count = zend_compile_args(args_ast, fbc, &may_have_extra_named_args);
3695+
3696+
zend_do_extended_fcall_begin();
3697+
3698+
opline = &CG(active_op_array)->opcodes[opnum_init];
3699+
opline->extended_value = arg_count;
3700+
3701+
if (opline->opcode == ZEND_INIT_FCALL) {
3702+
opline->op1.num = zend_vm_calc_used_stack(arg_count, fbc);
3703+
}
3704+
3705+
opline = zend_emit_op(result, zend_get_call_op(opline, fbc), NULL, NULL);
3706+
if (may_have_extra_named_args) {
3707+
opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS;
3708+
}
3709+
zend_do_extended_fcall_end();
3710+
return false;
37093711
}
37103712
/* }}} */
37113713

@@ -4494,6 +4496,7 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{
44944496
zend_op *opline;
44954497
zend_function *fbc = NULL;
44964498
bool nullsafe = ast->kind == ZEND_AST_NULLSAFE_METHOD_CALL;
4499+
uint32_t short_circuiting_checkpoint = zend_short_circuiting_checkpoint();
44974500

44984501
if (is_this_fetch(obj_ast)) {
44994502
if (this_guaranteed_exists()) {
@@ -4542,7 +4545,12 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{
45424545
}
45434546
}
45444547

4545-
zend_compile_call_common(result, args_ast, fbc);
4548+
if (zend_compile_call_common(result, args_ast, fbc)) {
4549+
if (short_circuiting_checkpoint != zend_short_circuiting_checkpoint()) {
4550+
zend_error_noreturn(E_COMPILE_ERROR,
4551+
"Cannot combine nullsafe operator with Closure creation");
4552+
}
4553+
}
45464554
}
45474555
/* }}} */
45484556

0 commit comments

Comments
 (0)