Hi Tim,

We will update the RFC, but here are a few answers:

On Wednesday, July 2nd, 2025 at 17:05, Tim Düsterhus <t...@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

Reply via email to