Hi Tim,
We will update the RFC, but here are a few answers:
On Wednesday, July 2nd, 2025 at 17:05, Tim Düsterhus <tim@bastelstu.be> wrote:
> I've now had a quick look at the implementation and the following
> questions came up that the RFC does not answer (and the tests in the PR
> do not obviously answer either):
>
> How will PFA calls appear in a stack trace and how will PFA Closures
> look like to var_dump(), Reflection, and to observers?
PFAs are instances of the Closure class (like FCCs), and will look
like a Closure to var_dump(), Reflection, and observers.
The Closure signature reflects the parameters that are accepted by the
PFA, not the underlying function (so it exposes only unbound
parameters).
function f(int $a, int $b) {
}
$f = f(?, 2);
echo new ReflectionFunction($f);
// Output:
Partial [ <user> function f ] {
@@ test.php 5 - 5
- Parameters [1] {
Parameter #0 [ <required> int $a ]
}
}
PFA Reflection is tested in Zend/tests/partial_application/reflection_*.phpt.
Parameter names, and which parameters are required, are defined by the
RFC. Currently, a few things are broken in the implementation,
including parameter default value reflection.
Additionally, var_dump() exposes bound and unbound args (with the
value of bound args). Currently the var_dump() output looks like
this:
object(Closure)#1 (5) {
["name"]=>
string(1) "f"
["file"]=>
string(%d) "test.php"
["line"]=>
int(7)
["parameter"]=>
array(1) {
["$a"]=>
string(10) "<required>"
}
["args"]=>
array(2) {
["a"]=>
NULL
["b"]=>
int(2)
}
}
PFAs do not appear in stack traces, only the function does.
> Classic FCC are 100% identical to the underlying function and thus can
> just “pretend” they are the underlying function, but that doesn't work
> for PFA. Consider the following:
>
> function foo(string $s, int $i) {
> var_dump($s, $i);
> }
>
> $f = foo("abc", ?);
>
> $f([]);
>
> How will the error message for the resulting TypeError look like?
Error messages refer to the underlying function as if it was called directly:
Uncaught TypeError: foo(): Argument #2 ($i) must be of type int,
array given, in test.php on line 7
The line number refers to the call site of the PFA ($f([])), not its
instantiation.
However, since PFAs must check argument count before binding them,
errors related to argument count refer to the PFA itself. Currently
the error message for $f() looks like this:
Uncaught Error: not enough arguments for application of foo, 0
given and exactly 1 expected, declared in test.php on line 5 in
test.php on line 7
> var_dump((new ReflectionFunction($f))->getName());
The underlying function name (like FCCs)
> var_dump((new ReflectionFunction($f))->getParameters());
See above
> is_callable($f, callable_name: $name);
> var_dump($name);
Closure::__invoke (like FCCs)
> function foo(string $s, #[\SensitiveParameter] int $i) {
> throw new \Exception();
> }
>
> $f = foo("abc", ?);
>
> $f(123);
>
> How will the stack trace look like? Does #[\\SensitiveParameter] work
> properly?
This is broken, but the intent is to support attributes, so that
SensitiveParameter and other attributes work as expected.
Best Regards,
Arnaud