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
