From 61e1f8aaebdd0f609ae6be5453d0bbab001cef12 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 3 Jun 2023 20:15:23 +0200 Subject: [PATCH] Let closure created from magic method accept named parameters Implements GH-11348. Closes GH-11364. --- NEWS | 2 + UPGRADING | 1 + .../trampoline_closure_named_arguments.phpt | 112 ++++++++++++++++++ Zend/zend_closures.c | 15 ++- 4 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 Zend/tests/trampoline_closure_named_arguments.phpt diff --git a/NEWS b/NEWS index 7034346b847..57b4b57bd26 100644 --- a/NEWS +++ b/NEWS @@ -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) diff --git a/UPGRADING b/UPGRADING index 53ac565428e..5c81ef8b325 100644 --- a/UPGRADING +++ b/UPGRADING @@ -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 diff --git a/Zend/tests/trampoline_closure_named_arguments.phpt b/Zend/tests/trampoline_closure_named_arguments.phpt new file mode 100644 index 00000000000..e209853e509 --- /dev/null +++ b/Zend/tests/trampoline_closure_named_arguments.phpt @@ -0,0 +1,112 @@ +--TEST-- +Trampoline closure created from magic method accepts named arguments +--FILE-- +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) { +} diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index e6ef6ca1d29..69eeb3cf1ce 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -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 { @@ -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;