Skip to content

Commit 4d0ccda

Browse files
committed
1 parent 9561a7f commit 4d0ccda

8 files changed

Lines changed: 130 additions & 45 deletions

Zend/tests/partial_application/errors_005.phpt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,17 @@ PFA errors: Can not fetch default parameter value for Closure::__invoke()
44
<?php
55

66
$f = function ($a, $b, $c = null) {};
7+
8+
$g = $f->__invoke(0, 0, ?);
9+
echo "No error\n";
10+
711
try {
8-
$g = $f->__invoke(0, 0, ?);
12+
$g = $f->__invoke(0, 0, ...);
913
} catch (Error $e) {
1014
echo $e::class, ": ", $e->getMessage(), "\n";
1115
}
1216

1317
?>
1418
--EXPECT--
19+
No error
1520
ArgumentCountError: Closure::__invoke(): Argument #3 ($c) must be passed explicitly, because the default value is not known

Zend/tests/partial_application/named_placeholders.phpt

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,32 @@ function foo($a = 1, $b = 2, $c = 3) {
1111
var_dump($a, $b, $c);
1212
}
1313

14+
echo "# Case 1\n";
15+
1416
$c = foo(b: ?);
1517

1618
echo (string) new ReflectionFunction($c);
1719

1820
$c(new B);
1921

22+
echo "# Case 2\n";
23+
2024
$c = $c(?);
2125

2226
echo (string) new ReflectionFunction($c);
2327

2428
$c(new B);
2529

26-
$c = foo(?, ?);
27-
$c = $c(b: ?);
30+
echo "# Case 3\n";
2831

29-
echo (string) new ReflectionFunction($c);
32+
$c = foo(?, ?);
33+
try {
34+
$c = $c(b: ?);
35+
} catch (\Throwable $e) {
36+
echo $e::class, ": ", $e->getMessage(), "\n";
37+
}
3038

31-
$c(new B);
39+
echo "# Case 4\n";
3240

3341
function bar($a = 1, $b = 2, ...$c) {
3442
var_dump($a, $b, $c);
@@ -40,66 +48,60 @@ echo (string) new ReflectionFunction($d);
4048

4149
$d(new B, new A, new C);
4250

51+
echo "# Case 5\n";
52+
4353
try {
4454
$d = bar(?, a: ?);
4555
} catch (\Throwable $e) {
46-
echo $e->getMessage(), "\n";
56+
echo $e::class, ": ", $e->getMessage(), "\n";
4757
}
4858

59+
echo "# Case 6\n";
60+
4961
try {
5062
$d = bar(c: ?, ...);
5163
} catch (\Throwable $e) {
52-
echo $e->getMessage(), "\n";
64+
echo $e::class, ": ", $e->getMessage(), "\n";
5365
}
5466

5567
?>
5668
--EXPECTF--
69+
# Case 1
5770
Closure [ <user> static function {closure:%s:%d} ] {
58-
@@ %snamed_placeholders.php 11 - 11
59-
60-
- Parameters [1] {
61-
Parameter #0 [ <optional> $b = 2 ]
62-
}
63-
}
64-
int(1)
65-
object(B)#%d (0) {
66-
}
67-
int(3)
68-
Closure [ <user> static function {closure:%s:%d} ] {
69-
@@ %snamed_placeholders.php 17 - 17
70-
71-
- Bound Variables [1] {
72-
Variable #0 [ $fn ]
73-
}
71+
@@ %snamed_placeholders.php 13 - 13
7472

7573
- Parameters [1] {
76-
Parameter #0 [ <optional> $b = 2 ]
74+
Parameter #0 [ <required> $b ]
7775
}
7876
}
7977
int(1)
8078
object(B)#%d (0) {
8179
}
8280
int(3)
81+
# Case 2
8382
Closure [ <user> static function {closure:%s:%d} ] {
84-
@@ %snamed_placeholders.php 24 - 24
83+
@@ %snamed_placeholders.php 21 - 21
8584

8685
- Bound Variables [1] {
8786
Variable #0 [ $fn ]
8887
}
8988

9089
- Parameters [1] {
91-
Parameter #0 [ <optional> $b = 2 ]
90+
Parameter #0 [ <required> $b ]
9291
}
9392
}
9493
int(1)
9594
object(B)#%d (0) {
9695
}
9796
int(3)
97+
# Case 3
98+
ArgumentCountError: {closure:pfa:%s:29}(): Argument #1 ($a) not passed
99+
# Case 4
98100
Closure [ <user> static function {closure:%s:%d} ] {
99-
@@ %snamed_placeholders.php 34 - 34
101+
@@ %snamed_placeholders.php 42 - 42
100102

101103
- Parameters [3] {
102-
Parameter #0 [ <optional> $b = 2 ]
104+
Parameter #0 [ <required> $b ]
103105
Parameter #1 [ <optional> $a = 1 ]
104106
Parameter #2 [ <optional> ...$c ]
105107
}
@@ -113,5 +115,7 @@ array(1) {
113115
object(C)#%d (0) {
114116
}
115117
}
116-
Named parameter $a overwrites previous placeholder
117-
Cannot use named placeholder for unknown or variadic parameter $c
118+
# Case 5
119+
Error: Named parameter $a overwrites previous placeholder
120+
# Case 6
121+
Error: Cannot use named placeholder for unknown or variadic parameter $c
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
--TEST--
2+
PFA optional placeholder: ? always generates required params; ... inherits optionality
3+
--FILE--
4+
<?php
5+
6+
function foo(int $a = 1, string $b = 'default', float $c = 3.14) {
7+
return [$a, $b, $c];
8+
}
9+
10+
echo "# ? makes targeted params required regardless of original optionality\n";
11+
$f = foo(?);
12+
var_dump((new ReflectionFunction($f))->getParameters()[0]->isOptional());
13+
14+
$f = foo(?, ?);
15+
$params = (new ReflectionFunction($f))->getParameters();
16+
var_dump($params[0]->isOptional(), $params[1]->isOptional());
17+
18+
$f = foo(?, ?, ?);
19+
$params = (new ReflectionFunction($f))->getParameters();
20+
var_dump($params[0]->isOptional(), $params[1]->isOptional(), $params[2]->isOptional());
21+
22+
echo "# ... inherits optionality from the original function\n";
23+
$f = foo(?, ...);
24+
$params = (new ReflectionFunction($f))->getParameters();
25+
var_dump($params[0]->isOptional(), $params[1]->isOptional(), $params[2]->isOptional());
26+
27+
$f = foo(?, ?, ...);
28+
$params = (new ReflectionFunction($f))->getParameters();
29+
var_dump($params[0]->isOptional(), $params[1]->isOptional(), $params[2]->isOptional());
30+
31+
echo "# Calling works: ? params are required, unspecified optional params use their defaults\n";
32+
$f = foo(?, ?);
33+
var_dump($f(10, 'hello'));
34+
35+
echo "# Named ? placeholders are also required\n";
36+
37+
function bar(int $x = 0, int $y = 0) {
38+
return [$x, $y];
39+
}
40+
41+
$f = bar(y: ?);
42+
var_dump((new ReflectionFunction($f))->getParameters()[0]->isOptional());
43+
var_dump($f(42));
44+
45+
?>
46+
--EXPECT--
47+
# ? makes targeted params required regardless of original optionality
48+
bool(false)
49+
bool(false)
50+
bool(false)
51+
bool(false)
52+
bool(false)
53+
bool(false)
54+
# ... inherits optionality from the original function
55+
bool(false)
56+
bool(true)
57+
bool(true)
58+
bool(false)
59+
bool(false)
60+
bool(true)
61+
# Calling works: ? params are required, unspecified optional params use their defaults
62+
array(3) {
63+
[0]=>
64+
int(10)
65+
[1]=>
66+
string(5) "hello"
67+
[2]=>
68+
float(3.14)
69+
}
70+
# Named ? placeholders are also required
71+
bool(false)
72+
array(2) {
73+
[0]=>
74+
int(0)
75+
[1]=>
76+
int(42)
77+
}

Zend/tests/partial_application/reflection_001.phpt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Closure [ <user> static function {closure:%s:%d} ] {
2323
@@ %sreflection_001.php 6 - 6
2424

2525
- Parameters [3] {
26-
Parameter #0 [ <optional> $a = 1 ]
26+
Parameter #0 [ <required> $a ]
2727
Parameter #1 [ <optional> $b = 5 ]
2828
Parameter #2 [ <optional> $c = 10 ]
2929
}
@@ -32,17 +32,17 @@ Closure [ <user> static function {closure:%s:%d} ] {
3232
@@ %sreflection_001.php 10 - 10
3333

3434
- Parameters [3] {
35-
Parameter #0 [ <optional> $a = 1 ]
36-
Parameter #1 [ <optional> $b = 5 ]
35+
Parameter #0 [ <required> $a ]
36+
Parameter #1 [ <required> $b ]
3737
Parameter #2 [ <optional> $c = 10 ]
3838
}
3939
}
4040
Closure [ <user> static function {closure:%s:%d} ] {
4141
@@ %sreflection_001.php 14 - 14
4242

4343
- Parameters [3] {
44-
Parameter #0 [ <optional> $a = 1 ]
45-
Parameter #1 [ <optional> $b = 5 ]
46-
Parameter #2 [ <optional> $c = 10 ]
44+
Parameter #0 [ <required> $a ]
45+
Parameter #1 [ <required> $b ]
46+
Parameter #2 [ <required> $c ]
4747
}
4848
}

Zend/tests/partial_application/rfc_examples_scoping.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,15 @@ class Unrelated {}
3434
$tests = [
3535
'Static closure 1' => [
3636
foo(?, ?),
37-
static fn(int $i, int $j = 0): string => foo($i, $j),
37+
static fn(int $i, int $j): string => foo($i, $j),
3838
],
3939
'Static closure 2' => [
4040
Foo::bar(?, ?),
4141
static fn(int $i, int $j): string => Foo::bar($i, $j),
4242
],
4343
'Static closure 3' => [
4444
foo(?, ?)(1, ?),
45-
static fn(int $j = 0): string => foo(1, $j),
45+
static fn(int $j): string => foo(1, $j),
4646
],
4747
'Static closure 4' => [
4848
foo(...)(?),

Zend/tests/partial_application/rfc_examples_semantics_examples.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,15 @@ $tests = [
5454
],
5555
'Provide some values, require the rest to be provided later (1)' => [
5656
stuff(1, 'hi', ?, ?, ?),
57-
static fn(float $f3, Point $p4, int $m5 = 0): array => stuff(1, 'hi', $f3, $p4, $m5),
57+
static fn(float $f3, Point $p4, int $m5): array => stuff(1, 'hi', $f3, $p4, $m5),
5858
],
5959
'Provide some values, require the rest to be provided later (2)' => [
6060
stuff(1, 'hi', ...),
6161
static fn(float $f3, Point $p4, int $m5 = 0): array => stuff(1, 'hi', $f3, $p4, $m5),
6262
],
6363
'Provide some values, but not just from the left (1)' => [
6464
stuff(1, ?, 3.5, ?, ?),
65-
static fn(string $s2, Point $p4, int $m5 = 0): array => stuff(1, $s2, 3.5, $p4, $m5),
65+
static fn(string $s2, Point $p4, int $m5): array => stuff(1, $s2, 3.5, $p4, $m5),
6666
],
6767
'Provide some values, but not just from the left (2)' => [
6868
stuff(1, ?, 3.5, ...),

Zend/tests/partial_application/variation_debug_001.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,6 @@ object(Closure)#%d (5) {
3535
["parameter"]=>
3636
array(1) {
3737
["$a"]=>
38-
string(10) "<optional>"
38+
string(10) "<required>"
3939
}
4040
}

Zend/zend_partial.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ static zend_ast *zp_compile_forwarding_call(
578578
}
579579
zend_ast *default_value_ast;
580580
if (Z_TYPE(default_value) == IS_CONSTANT_AST) {
581-
/* Must dup AST because we are doing to destroy it */
581+
/* Must dup AST because we are going to destroy it */
582582
default_value_ast = zend_ast_dup(Z_ASTVAL(default_value));
583583
} else {
584584
default_value_ast = zend_ast_create_zval(&default_value);
@@ -766,11 +766,10 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
766766
}
767767

768768
arg_to_param_offset_map[arg_offset] = param_offset;
769-
770-
num_required = zp_compute_num_required(function,
771-
arg_offset, param_offset, num_required);
772769
}
773770

771+
num_required = num_params;
772+
774773
/* Handle implicit placeholders added by '...' */
775774
if (uses_variadic_placeholder) {
776775
for (uint32_t arg_offset = 0; arg_offset < new_argc; arg_offset++) {

0 commit comments

Comments
 (0)