Skip to content

Commit

Permalink
Let closure created from magic method accept named parameters
Browse files Browse the repository at this point in the history
Implements GH-11348.

Closes GH-11364.
  • Loading branch information
nielsdos committed Jun 5, 2023
1 parent 16a63d7 commit 61e1f8a
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 2 deletions.
2 changes: 2 additions & 0 deletions NEWS
Expand Up @@ -40,6 +40,8 @@ PHP NEWS
. Fix bug #79836 (Segfault in concat_function). (nielsdos)
. Fix bug #81705 (type confusion/UAF on set_error_handler with concat
operation). (nielsdos)
. Fix GH-11348 (Closure created from magic method does not accept named
arguments). (nielsdos)

- Date:
. Implement More Appropriate Date/Time Exceptions RFC. (Derick)
Expand Down
1 change: 1 addition & 0 deletions UPGRADING
Expand Up @@ -58,6 +58,7 @@ PHP 8.3 UPGRADE NOTES
RFC: https://wiki.php.net/rfc/readonly_amendments
. Class, interface, trait, and enum constants now support type
declarations. RFC: https://wiki.php.net/rfc/typed_class_constants
. Closures created from magic methods can now accept named arguments.

- Posix
. posix_getrlimit() now takes an optional $res parameter to allow fetching a
Expand Down
112 changes: 112 additions & 0 deletions Zend/tests/trampoline_closure_named_arguments.phpt
@@ -0,0 +1,112 @@
--TEST--
Trampoline closure created from magic method accepts named arguments
--FILE--
<?php

class Test {
public function __call($name, $args) {
var_dump($name, $args);
}
public static function __callStatic($name, $args) {
var_dump($name, $args);
}
}

$test = new Test;

echo "-- Non-static cases --\n";
$test->test(1, 2, a: 123);
$test->test(...)(1, 2);
$test->test(...)(1, 2, a: 123, b: $test);
$test->test(...)(a: 123, b: $test);
$test->test(...)();

echo "-- Static cases --\n";
Test::testStatic(1, 2, a: 123);
Test::testStatic(...)(1, 2);
Test::testStatic(...)(1, 2, a: 123, b: $test);
Test::testStatic(...)(a: 123, b: $test);
Test::testStatic(...)();

?>
--EXPECT--
-- Non-static cases --
string(4) "test"
array(3) {
[0]=>
int(1)
[1]=>
int(2)
["a"]=>
int(123)
}
string(4) "test"
array(2) {
[0]=>
int(1)
[1]=>
int(2)
}
string(4) "test"
array(4) {
[0]=>
int(1)
[1]=>
int(2)
["a"]=>
int(123)
["b"]=>
object(Test)#1 (0) {
}
}
string(4) "test"
array(2) {
["a"]=>
int(123)
["b"]=>
object(Test)#1 (0) {
}
}
string(4) "test"
array(0) {
}
-- Static cases --
string(10) "testStatic"
array(3) {
[0]=>
int(1)
[1]=>
int(2)
["a"]=>
int(123)
}
string(10) "testStatic"
array(2) {
[0]=>
int(1)
[1]=>
int(2)
}
string(10) "testStatic"
array(4) {
[0]=>
int(1)
[1]=>
int(2)
["a"]=>
int(123)
["b"]=>
object(Test)#1 (0) {
}
}
string(10) "testStatic"
array(2) {
["a"]=>
int(123)
["b"]=>
object(Test)#1 (0) {
}
}
string(10) "testStatic"
array(0) {
}
15 changes: 13 additions & 2 deletions Zend/zend_closures.c
Expand Up @@ -294,7 +294,18 @@ static ZEND_NAMED_FUNCTION(zend_closure_call_magic) /* {{{ */ {
fci.params = params;
fci.param_count = 2;
ZVAL_STR(&fci.params[0], EX(func)->common.function_name);
if (ZEND_NUM_ARGS()) {
if (EX_CALL_INFO() & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
zend_string *name;
zval *named_param_zval;
array_init_size(&fci.params[1], ZEND_NUM_ARGS() + zend_hash_num_elements(EX(extra_named_params)));
/* Avoid conversion from packed to mixed later. */
zend_hash_real_init_mixed(Z_ARRVAL(fci.params[1]));
zend_copy_parameters_array(ZEND_NUM_ARGS(), &fci.params[1]);
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(EX(extra_named_params), name, named_param_zval) {
Z_TRY_ADDREF_P(named_param_zval);
zend_hash_add_new(Z_ARRVAL(fci.params[1]), name, named_param_zval);
} ZEND_HASH_FOREACH_END();
} else if (ZEND_NUM_ARGS()) {
array_init_size(&fci.params[1], ZEND_NUM_ARGS());
zend_copy_parameters_array(ZEND_NUM_ARGS(), &fci.params[1]);
} else {
Expand Down Expand Up @@ -841,7 +852,7 @@ void zend_closure_from_frame(zval *return_value, zend_execute_data *call) { /* {

memset(&trampoline, 0, sizeof(zend_internal_function));
trampoline.type = ZEND_INTERNAL_FUNCTION;
trampoline.fn_flags = mptr->common.fn_flags & ZEND_ACC_STATIC;
trampoline.fn_flags = mptr->common.fn_flags & (ZEND_ACC_STATIC | ZEND_ACC_VARIADIC);
trampoline.handler = zend_closure_call_magic;
trampoline.function_name = mptr->common.function_name;
trampoline.scope = mptr->common.scope;
Expand Down

0 comments on commit 61e1f8a

Please sign in to comment.