Skip to content
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 @@ -18,6 +18,7 @@ PHP NEWS
- Standard:
. Fix GH-19610 (Deprecation warnings in functions taking as argument).
(Girgias)
. Added func_get_named_args() function. (alexandre-daubois)

- URI:
. Fixed memory management of Uri\WhatWg\Url objects. (timwolla)
Expand Down
1 change: 1 addition & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,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
Loading