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