Thanks for explaining it better than I did. Regarding the implementation, that was roughly what I was thinking.
But can't we put extension methods second, after real methods but before __call? As far as I understand, the reason to put it after __call is to avoid a performance penalty on __call calls, but this would mean extension methods are not possible for classes that implement __call. Not a huge deal, but a thing to consider. On Wed, Aug 10, 2022 at 8:32 PM Rowan Tommins <rowan.coll...@gmail.com> wrote: > On Wed, 10 Aug 2022 at 17:18, Ben Ramsey <ram...@php.net> wrote: > > > I believe this is also called "monkey patching" in some places, and > > Ruby, Python, and JavaScript all offer some form of object extension > > similar to this. > > > > There is also the PHP runkit extension that provides some of the > > functionality you've described: https://www.php.net/runkit7 > > > > > Monkey-patching generally refers to the ability to completely "re-open" a > class, and implement additional behaviour (or even change existing > behaviour) *with the same privileges as the original definition*. > > Extension methods are a much more constrained feature - they don't break > the class's encapsulation, only provide an extra syntax for operations that > would already be legal. In C#, for instance, calling > "foo.someExtensionMethod(bar)" is just syntactic sugar for the static > method call "SomeClass.someExtensionMethod(foo, bar)", and cannot hide or > over-ride a real method with the same name. > > The challenge in PHP is that so little is resolved at compile-time. > Adapting the example from the first post: > > namespace App\Business; > use extension App\CollectionExtension::map on Collection; > > function foo($x) { > $x > ->map(fn ($value) => $value + 1) > ->map(fn ($value) => $value * 2); > } > > PHP doesn't know until run-time: > > * whether App\CollectionExtension actually exists (the compiler does not > have access to an autoloader) > * whether it defines a "map" method, and applies to type Collection, even > if the user has *claimed* that in the "use" statement > * what type $x will be; even with a type constraint of "Collection $x", it > might be a sub-type, changing the answer to the following question > * whether that class already contains a "map" method, or an __call handler > > I think we would have to implement it as a final fall-back for missing > methods, between __call and throwing an error: > > 1. at compile-time, build a list of in-scope extensions methods; note that > these would just be strings at this point, not loaded code > 2. just before throwing a "method not found" error, loop over the list, > autoloading each entry if necessary; possibly at this point, errors would > be raised for naming conflicts and other violations > 3. check each in-scope extension method in turn for an "instanceof" match > against the current object > 4. if one matches, despatch the call > 5. if none matches, throw an error as normal > > Regards, > -- > Rowan Tommins > [IMSoP] >