On Sunday, 29 March 2026 at 22:45, Osama Aldemeery <[email protected]> wrote:

> Hi all,
>
> I'd like to propose an Invokable interface, the Stringable equivalent for 
> __invoke().
>
> The idea has come up a few times over the years (most recently in the PR 
> #15492 discussion, where Gina suggested this exact approach) but never had a 
> concrete implementation.
>
> I've put one together: https://github.com/php/php-src/pull/21574
>
> It follows the Stringable pattern: auto-implemented for any class defining 
> __invoke(), explicitly implementable with enforcement, and covariant to 
> callable in return type checks.
>
> I'm working on a formal RFC and would love feedback before posting it.
> I'd also like to request wiki karma to create the RFC page — my wiki username 
> is aldemeery

I already replied on the PR, but I am very much *not* in favour of this 
proposal.
My comment in PR #15492 [1] specifically says that we don't have such an 
interface, and I don't want to add duck typing as this goes against PHP's 
nominal typing system.
The discussion around such an interface happened in PR #18161 [2].
However, Tim noted that we cannot have an interface the defines the signature 
of __invoke().
And then Ilija suggested a marker interface similar to Throwabale.

In the year since this discussion has happened I've had time to think and the 
reason I never even attempted to move this forward is that this is, IMHO, 
repeating the same mistake of `Stringable`.

The only reason for `Stringable` to exists is that `strict_types` exists, and 
when enabled prevents objects with a `__toString()` method to be passed to 
`string` types.
This causes nonsensical design choices of "should I mark my parameters as 
`string|Stringable` or not" when it should just always be `string` and let the 
engine do the type juggling.

The proposal of adding an `Invokable` interfaces reproduces this exact same 
mistake.
One shouldn't care that the callable is an object with an invoke method? Why 
could it not be a callable array or be a callable string?
As the consumer of such an argument the representation of a callable shouldn't 
matter.

The main argument seems to be that the callable type cannot be used on property 
types.
There are 2 reasons why this is the case.
The one most people know is that string/array callables is because there are 
scope visibility implications.
(See https://3v4l.org/hCpiG for an example.)
However, the **primary** reason is because of partially supported callables 
that have been deprecated in PHP 8.2. [3]
Those partially supported callables don't just depend on *where* they are 
created but also where they are *used*.

When those partially supported callables are removed in PHP 9, it seems very 
feasible to allow `callable` to be used as a property type.
The mechanism to do so would be to *effectively* convert any "legacy" (array, 
string, object with __invoke methods) callables into a `Closure` object during 
the type check.
This would remove any scope visibility issues from callables created within 
methods.
(As an aside I firmly believe this behaviour would reduce the engine complexity 
around callables)

Moreover, I don't believe that a magic interface that does effectively nothing 
is good language design,
and that this is trying to fix a "problem" in a way that is going to cause more 
problems down the line rather than wait for the proper solution of fixing the 
`callable` type.
Similarly to how I feel about the introduction of `Stringable`, where the 
proper solution IMHO is to unify PHP's typing modes. [4]

Finally, with all the syntactic improvement in PHP, creating a Closure object 
to go deal with the current limitations of the callable type feels like a 
non-issue.

Best regards,
Gina P. Banyard
[1] https://github.com/php/php-src/pull/15492
[2] https://github.com/php/php-src/issues/18161
[3] https://wiki.php.net/rfc/deprecate_partially_supported_callables
[4] https://github.com/Girgias/unify-typing-modes-rfc

Reply via email to