Skip to content

Add func_get_named_args() #19329

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ PHP NEWS
(nielsdos)
. Optimized PHP html_entity_decode function. (Artem Ukrainskiy)
. Minor optimization to array_chunk(). (nielsdos)
. Added func_get_named_args() function. (alexandre-daubois)

- URI:
. Empty host handling is fixed. (Máté Kocsis)
Expand Down
1 change: 1 addition & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,7 @@ PHP 8.5 UPGRADE NOTES
- Standard:
. Added array_first() and array_last().
RFC: https://wiki.php.net/rfc/array_first_last
. Added func_get_named_args() function.

========================================
7. New Classes and Interfaces
Expand Down
1 change: 1 addition & 0 deletions Zend/Optimizer/dce.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ static inline bool may_have_side_effects(
case ZEND_IN_ARRAY:
case ZEND_FUNC_NUM_ARGS:
case ZEND_FUNC_GET_ARGS:
case ZEND_FUNC_GET_NAMED_ARGS:
case ZEND_ARRAY_KEY_EXISTS:
/* No side effects */
return 0;
Expand Down
1 change: 1 addition & 0 deletions Zend/Optimizer/zend_cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ ZEND_API void zend_build_cfg(zend_arena **arena, const zend_op_array *op_array,
}
break;
case ZEND_FUNC_GET_ARGS:
case ZEND_FUNC_GET_NAMED_ARGS:
flags |= ZEND_FUNC_VARARG;
break;
case ZEND_EXT_STMT:
Expand Down
1 change: 1 addition & 0 deletions Zend/Optimizer/zend_func_infos.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ static const func_info_t func_infos[] = {
F1("clone", MAY_BE_OBJECT),
F1("zend_version", MAY_BE_STRING),
FN("func_get_args", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_ANY),
FN("func_get_named_args", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY),
F1("get_class_vars", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF),
F1("get_class_methods", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING),
F1("get_included_files", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING),
Expand Down
2 changes: 2 additions & 0 deletions Zend/Optimizer/zend_inference.c
Original file line number Diff line number Diff line change
Expand Up @@ -3926,6 +3926,7 @@ static zend_always_inline zend_result _zend_update_type_info(
UPDATE_SSA_TYPE(MAY_BE_LONG, ssa_op->result_def);
break;
case ZEND_FUNC_GET_ARGS:
case ZEND_FUNC_GET_NAMED_ARGS:
UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ARRAY|MAY_BE_ARRAY_EMPTY|MAY_BE_ARRAY_PACKED|MAY_BE_ARRAY_OF_ANY, ssa_op->result_def);
break;
case ZEND_GET_CLASS:
Expand Down Expand Up @@ -5025,6 +5026,7 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
case ZEND_ISSET_ISEMPTY_CV:
case ZEND_FUNC_NUM_ARGS:
case ZEND_FUNC_GET_ARGS:
case ZEND_FUNC_GET_NAMED_ARGS:
case ZEND_COPY_TMP:
case ZEND_JMP_NULL:
case ZEND_JMP_FRAMELESS:
Expand Down
2 changes: 2 additions & 0 deletions Zend/Optimizer/zend_optimizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,8 @@ uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args)
return ZEND_FUNC_VARARG;
} else if (zend_string_equals_literal(name, "func_get_args")) {
return ZEND_FUNC_VARARG;
} else if (zend_string_equals_literal(name, "func_get_named_args")) {
return ZEND_FUNC_VARARG;
} else {
return 0;
}
Expand Down
82 changes: 82 additions & 0 deletions Zend/tests/func_get_named_args.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
--TEST--
Testing func_get_named_args()
--FILE--
<?php

function test($a, $b = 'default_b', $c = 'default_c') {
return func_get_named_args();
}

var_dump(test('A', 'B', 'C'));
var_dump(test(c: 'C', a: 'A'));
var_dump(test('A', c: 'C'));

function variadic_test($a, $b, ...$rest) {
return func_get_named_args();
}

var_dump(variadic_test('A', 'B', 'C', 'D'));

function no_args() {
return func_get_named_args();
}

var_dump(no_args());

try {
func_get_named_args();
} catch (Error $e) {
echo "Error: " . $e->getMessage() . "\n";
}

function by_ref($a, &$b) {
return func_get_named_args();
}

var_dump(by_ref('A', $b));

?>
--EXPECT--
array(3) {
["a"]=>
string(1) "A"
["b"]=>
string(1) "B"
["c"]=>
string(1) "C"
}
array(3) {
["a"]=>
string(1) "A"
["b"]=>
string(9) "default_b"
["c"]=>
string(1) "C"
}
array(3) {
["a"]=>
string(1) "A"
["b"]=>
string(9) "default_b"
["c"]=>
string(1) "C"
}
array(4) {
["a"]=>
string(1) "A"
["b"]=>
string(1) "B"
[2]=>
string(1) "C"
[3]=>
string(1) "D"
}
array(0) {
}
Error: func_get_named_args() cannot be called from the global scope
array(2) {
["a"]=>
string(1) "A"
["b"]=>
NULL
}
106 changes: 106 additions & 0 deletions Zend/tests/func_get_named_args_closures.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
--TEST--
Testing func_get_named_args() with anonymous functions and closures
--FILE--
<?php

$anonymousFunc = function($a, $b = 'default_b', $c = 'default_c') {
return func_get_named_args();
};

var_dump($anonymousFunc('A', 'B', 'C'));
var_dump($anonymousFunc(c: 'C', a: 'A'));
var_dump($anonymousFunc('A', c: 'C'));

$capturedVar = 'captured';
$closure = function($x, $y = 'default_y') use ($capturedVar) {
return func_get_named_args();
};

var_dump($closure('X', 'Y'));
var_dump($closure(y: 'Y', x: 'X'));

$variadicFunc = function($first, ...$rest) {
return func_get_named_args();
};

var_dump($variadicFunc('FIRST', 'extra1', 'extra2'));
var_dump($variadicFunc(first: 'FIRST', extra: 'EXTRA'));

$arrow = fn($p, $q = 'default_q') => func_get_named_args();

var_dump($arrow('P', 'Q'));
var_dump($arrow(q: 'Q', p: 'P'));

$mapped = array_map(function($item, $prefix = 'prefix') {
return func_get_named_args();
}, ['test']);
var_dump($mapped[0]);

?>
--EXPECT--
array(3) {
["a"]=>
string(1) "A"
["b"]=>
string(1) "B"
["c"]=>
string(1) "C"
}
array(3) {
["a"]=>
string(1) "A"
["b"]=>
string(9) "default_b"
["c"]=>
string(1) "C"
}
array(3) {
["a"]=>
string(1) "A"
["b"]=>
string(9) "default_b"
["c"]=>
string(1) "C"
}
array(2) {
["x"]=>
string(1) "X"
["y"]=>
string(1) "Y"
}
array(2) {
["x"]=>
string(1) "X"
["y"]=>
string(1) "Y"
}
array(3) {
["first"]=>
string(5) "FIRST"
[1]=>
string(6) "extra1"
[2]=>
string(6) "extra2"
}
array(2) {
["first"]=>
string(5) "FIRST"
["extra"]=>
string(5) "EXTRA"
}
array(2) {
["p"]=>
string(1) "P"
["q"]=>
string(1) "Q"
}
array(2) {
["p"]=>
string(1) "P"
["q"]=>
string(1) "Q"
}
array(1) {
["item"]=>
string(4) "test"
}
87 changes: 87 additions & 0 deletions Zend/tests/func_get_named_args_methods.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
--TEST--
Testing func_get_named_args() with methods
--FILE--
<?php

class TestClass {
public function instanceMethod($a, $b = 'default_b', $c = 'default_c') {
return func_get_named_args();
}

public static function staticMethod($x, $y = 'default_y', $z = 'default_z') {
return func_get_named_args();
}

public function variadicMethod($first, ...$rest) {
return func_get_named_args();
}
}

$obj = new TestClass();

var_dump($obj->instanceMethod('A', 'B', 'C'));
var_dump($obj->instanceMethod(c: 'C', a: 'A'));
var_dump($obj->instanceMethod('A', c: 'C'));

var_dump(TestClass::staticMethod('X', 'Y', 'Z'));
var_dump(TestClass::staticMethod(z: 'Z', x: 'X'));

var_dump($obj->variadicMethod('FIRST', 'extra1', 'extra2'));
var_dump($obj->variadicMethod(first: 'FIRST', extra: 'EXTRA'));

?>
--EXPECT--
array(3) {
["a"]=>
string(1) "A"
["b"]=>
string(1) "B"
["c"]=>
string(1) "C"
}
array(3) {
["a"]=>
string(1) "A"
["b"]=>
string(9) "default_b"
["c"]=>
string(1) "C"
}
array(3) {
["a"]=>
string(1) "A"
["b"]=>
string(9) "default_b"
["c"]=>
string(1) "C"
}
array(3) {
["x"]=>
string(1) "X"
["y"]=>
string(1) "Y"
["z"]=>
string(1) "Z"
}
array(3) {
["x"]=>
string(1) "X"
["y"]=>
string(9) "default_y"
["z"]=>
string(1) "Z"
}
array(3) {
["first"]=>
string(5) "FIRST"
[1]=>
string(6) "extra1"
[2]=>
string(6) "extra2"
}
array(2) {
["first"]=>
string(5) "FIRST"
["extra"]=>
string(5) "EXTRA"
}
49 changes: 49 additions & 0 deletions Zend/tests/func_get_named_args_spread.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
--TEST--
Testing func_get_named_args() with spread operator
--FILE--
<?php

function hello_named(string $name, int $age, ...$args): array {
return func_get_named_args();
}

var_dump(hello_named(...[
'age' => 30,
'name' => 'John',
'extra' => 'data',
]));

var_dump(hello_named(name: 'John', age: 30, extra: 'data'));

var_dump(hello_named(...[
'name' => 'Bob',
'age' => 35,
'occupation' => 'Developer',
]));

?>
--EXPECT--
array(3) {
["name"]=>
string(4) "John"
["age"]=>
int(30)
["extra"]=>
string(4) "data"
}
array(3) {
["name"]=>
string(4) "John"
["age"]=>
int(30)
["extra"]=>
string(4) "data"
}
array(3) {
["name"]=>
string(3) "Bob"
["age"]=>
int(35)
["occupation"]=>
string(9) "Developer"
}
Loading