Skip to content

Commit d92229d

Browse files
committed
Implement named parameters
From an engine perspective, named parameters mainly add three concepts: * The SEND_* opcodes now accept a CONST op2, which is the argument name. For now, it is looked up by linear scan and runtime cached. * This may leave UNDEF arguments on the stack. To avoid having to deal with them in other places, a CHECK_UNDEF_ARGS opcode is used to either replace them with defaults, or error. * For variadic functions, EX(extra_named_params) are collected and need to be freed based on ZEND_CALL_HAS_EXTRA_NAMED_PARAMS. RFC: https://wiki.php.net/rfc/named_params Closes GH-5357.
1 parent 9a71d47 commit d92229d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+5970
-2521
lines changed

UPGRADING

+2
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,8 @@ PHP 8.0 UPGRADE NOTES
640640
RFC: https://wiki.php.net/rfc/inheritance_private_methods
641641
. Added support for nullsafe operator (`?->`).
642642
RFC: https://wiki.php.net/rfc/nullsafe_operator
643+
. Added support for named arguments.
644+
RFC: https://wiki.php.net/rfc/named_params
643645

644646
- Date:
645647
. Added DateTime::createFromInterface() and

Zend/tests/arg_unpack/non_integer_keys.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ try {
1818

1919
?>
2020
--EXPECT--
21-
Exception: Cannot unpack Traversable with non-integer keys
21+
Exception: Keys must be of type int|string during argument unpacking

Zend/tests/arg_unpack/string_keys.phpt

+1-7
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,6 @@ set_error_handler(function($errno, $errstr) {
77
var_dump($errstr);
88
});
99

10-
try {
11-
var_dump(...[1, 2, "foo" => 3, 4]);
12-
} catch (Error $ex) {
13-
var_dump($ex->getMessage());
14-
}
1510
try {
1611
var_dump(...new ArrayIterator([1, 2, "foo" => 3, 4]));
1712
} catch (Error $ex) {
@@ -20,5 +15,4 @@ try {
2015

2116
?>
2217
--EXPECT--
23-
string(36) "Cannot unpack array with string keys"
24-
string(42) "Cannot unpack Traversable with string keys"
18+
string(68) "Cannot use positional argument after named argument during unpacking"

Zend/tests/assert/expect_015.phpt

+2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ assert(0 && ($a = function &(array &$a, ?X $b = null) use ($c,&$d) : ?X {
6060
$x = C::${$z . "_1"};
6161
$x?->y;
6262
$x?->y();
63+
foo(bar: $x);
6364
}
6465
}
6566
}));
@@ -202,6 +203,7 @@ Warning: assert(): assert(0 && ($a = function &(array &$a, ?X $b = null) use($c,
202203
$x = C::${$z . '_1'};
203204
$x?->y;
204205
$x?->y();
206+
foo(bar: $x);
205207
}
206208

207209
}

Zend/tests/bug43343.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ $foo = 'bar';
88
var_dump(new namespace::$foo);
99
?>
1010
--EXPECTF--
11-
Parse error: syntax error, unexpected token "namespace" in %s on line %d
11+
Parse error: syntax error, unexpected token "namespace", expecting ":" in %s on line %d

Zend/tests/named_params/__call.phpt

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--TEST--
2+
Check that __call() and __callStatic() work with named parameters
3+
--FILE--
4+
<?php
5+
6+
class Test {
7+
public function __call(string $method, array $args) {
8+
$this->{'_'.$method}(...$args);
9+
}
10+
11+
public static function __callStatic(string $method, array $args) {
12+
(new static)->{'_'.$method}(...$args);
13+
}
14+
15+
private function _method($a = 'a', $b = 'b') {
16+
echo "a: $a, b: $b\n";
17+
}
18+
}
19+
20+
$obj = new class { public function __toString() { return "STR"; } };
21+
22+
$test = new Test;
23+
$test->method(a: 'A', b: 'B');
24+
$test->method(b: 'B');
25+
$test->method(b: $obj);
26+
Test::method(a: 'A', b: 'B');
27+
Test::method(b: 'B');
28+
Test::method(b: $obj);
29+
30+
?>
31+
--EXPECT--
32+
a: A, b: B
33+
a: a, b: B
34+
a: a, b: STR
35+
a: A, b: B
36+
a: a, b: B
37+
a: a, b: STR

Zend/tests/named_params/__invoke.phpt

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
--TEST--
2+
Check that __invoke() works with named parameters
3+
--FILE--
4+
<?php
5+
6+
class Test {
7+
public function __invoke($a = 'a', $b = 'b') {
8+
echo "a: $a, b: $b\n";
9+
}
10+
}
11+
12+
class Test2 {
13+
public function __invoke($a = 'a', $b = 'b', ...$rest) {
14+
echo "a: $a, b: $b\n";
15+
var_dump($rest);
16+
}
17+
}
18+
19+
$test = new Test;
20+
$test(b: 'B', a: 'A');
21+
$test(b: 'B');
22+
try {
23+
$test(b: 'B', c: 'C');
24+
} catch (Error $e) {
25+
echo $e->getMessage(), "\n";
26+
}
27+
echo "\n";
28+
29+
$test2 = new Test2;
30+
$test2(b: 'B', a: 'A', c: 'C');
31+
$test2(b: 'B', c: 'C');
32+
echo "\n";
33+
34+
$test3 = function($a = 'a', $b = 'b') {
35+
echo "a: $a, b: $b\n";
36+
};
37+
$test3(b: 'B', a: 'A');
38+
$test3(b: 'B');
39+
try {
40+
$test3(b: 'B', c: 'C');
41+
} catch (Error $e) {
42+
echo $e->getMessage(), "\n";
43+
}
44+
45+
?>
46+
--EXPECT--
47+
a: A, b: B
48+
a: a, b: B
49+
Unknown named parameter $c
50+
51+
a: A, b: B
52+
array(1) {
53+
["c"]=>
54+
string(1) "C"
55+
}
56+
a: a, b: B
57+
array(1) {
58+
["c"]=>
59+
string(1) "C"
60+
}
61+
62+
a: A, b: B
63+
a: a, b: B
64+
Unknown named parameter $c
+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
--TEST--
2+
Named params in attributes
3+
--FILE--
4+
<?php
5+
6+
@@Attribute
7+
class MyAttribute {
8+
public function __construct(
9+
public $a = 'a',
10+
public $b = 'b',
11+
public $c = 'c',
12+
) {}
13+
}
14+
15+
@@MyAttribute('A', c: 'C')
16+
class Test1 {}
17+
18+
@@MyAttribute('A', a: 'C')
19+
class Test2 {}
20+
21+
$attr = (new ReflectionClass(Test1::class))->getAttributes()[0];
22+
var_dump($attr->getName());
23+
var_dump($attr->getArguments());
24+
var_dump($attr->newInstance());
25+
26+
$attr = (new ReflectionClass(Test2::class))->getAttributes()[0];
27+
try {
28+
var_dump($attr->newInstance());
29+
} catch (Error $e) {
30+
echo $e->getMessage(), "\n";
31+
}
32+
33+
?>
34+
--EXPECT--
35+
string(11) "MyAttribute"
36+
array(2) {
37+
[0]=>
38+
string(1) "A"
39+
["c"]=>
40+
string(1) "C"
41+
}
42+
object(MyAttribute)#1 (3) {
43+
["a"]=>
44+
string(1) "A"
45+
["b"]=>
46+
string(1) "b"
47+
["c"]=>
48+
string(1) "C"
49+
}
50+
Named parameter $a overwrites previous argument
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Named params in attributes: Duplicate named parameter error
3+
--FILE--
4+
<?php
5+
6+
@@MyAttribute(a: 'A', a: 'A')
7+
class Test {}
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Duplicate named parameter $a in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
Named flags parameter for Attribute attribute
3+
--FILE--
4+
<?php
5+
6+
@@Attribute(flags: Attribute::TARGET_CLASS)
7+
class MyAttribute {
8+
}
9+
10+
@@MyAttribute
11+
function test() {}
12+
13+
(new ReflectionFunction('test'))->getAttributes()[0]->newInstance();
14+
15+
?>
16+
--EXPECTF--
17+
Fatal error: Uncaught Error: Attribute "MyAttribute" cannot target function (allowed targets: class) in %s:%d
18+
Stack trace:
19+
#0 %s(%d): ReflectionAttribute->newInstance()
20+
#1 {main}
21+
thrown in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
Named flags parameter for Attribute attribute (incorrect parameter name)
3+
--FILE--
4+
<?php
5+
6+
// TODO: This should error at compile-time.
7+
@@Attribute(not_flags: Attribute::TARGET_CLASS)
8+
class MyAttribute {
9+
}
10+
11+
@@MyAttribute
12+
function test() {}
13+
14+
(new ReflectionFunction('test'))->getAttributes()[0]->newInstance();
15+
16+
?>
17+
--EXPECTF--
18+
Fatal error: Uncaught Error: Attribute "MyAttribute" cannot target function (allowed targets: class) in %s:%d
19+
Stack trace:
20+
#0 %s(%d): ReflectionAttribute->newInstance()
21+
#1 {main}
22+
thrown in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
Named params in attributes: Positional after named error
3+
--FILE--
4+
<?php
5+
6+
@@Attribute
7+
class MyAttribute { }
8+
9+
@@MyAttribute(a: 'A', 'B')
10+
class Test {}
11+
12+
?>
13+
--EXPECTF--
14+
Fatal error: Cannot use positional argument after named argument in %s on line %d
+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
--TEST--
2+
Extra named params in backtraces
3+
--FILE--
4+
<?php
5+
6+
function test($a, ...$rest) {
7+
var_dump(debug_backtrace());
8+
debug_print_backtrace();
9+
throw new Exception("Test");
10+
}
11+
12+
try {
13+
test(1, 2, x: 3, y: 4);
14+
} catch (Exception $e) {
15+
var_dump($e->getTrace());
16+
echo $e, "\n";
17+
}
18+
19+
?>
20+
--EXPECTF--
21+
array(1) {
22+
[0]=>
23+
array(4) {
24+
["file"]=>
25+
string(%d) "%s"
26+
["line"]=>
27+
int(10)
28+
["function"]=>
29+
string(4) "test"
30+
["args"]=>
31+
array(4) {
32+
[0]=>
33+
int(1)
34+
[1]=>
35+
int(2)
36+
["x"]=>
37+
int(3)
38+
["y"]=>
39+
int(4)
40+
}
41+
}
42+
}
43+
#0 test(1, 2, x: 3, y: 4) called at [%s:10]
44+
array(1) {
45+
[0]=>
46+
array(4) {
47+
["file"]=>
48+
string(%d) "%s"
49+
["line"]=>
50+
int(10)
51+
["function"]=>
52+
string(4) "test"
53+
["args"]=>
54+
array(4) {
55+
[0]=>
56+
int(1)
57+
[1]=>
58+
int(2)
59+
["x"]=>
60+
int(3)
61+
["y"]=>
62+
int(4)
63+
}
64+
}
65+
}
66+
Exception: Test in %s:%d
67+
Stack trace:
68+
#0 %s(%d): test(1, 2, x: 3, y: 4)
69+
#1 {main}

0 commit comments

Comments
 (0)