On Thu, Oct 1, 2026 at 8:52 AM Larry Garfield <[email protected]> wrote:
> On Wed, Sep 30, 2026, at 7:29 PM, Karoly Negyesi wrote: > > Hello, > > > > I submitted a draft RFC to https://github.com/php/php-src/pull/24030. > > The PR contains an implementation and tests (please be gentle, I > > haven't written C in decades). > > > > I propose an "implements by" syntax to automate delegation, closely > > modelled on Kotlin. > > > > In short, > > > > interface T { function foo(); } > > class A implements T { function foo() { print "A"; } } > > class C implements T by $t { function __construct(private T $t) {} } > > new C(new A)->foo(); > > > > The performance impact should be minimal as everything happens at > > compile time and during class linking with no runtime changes. There is > > no BC break. (That's the intention, at least. I modelled the change > > after enum.) > > > > Regards > > > > Karoly Negyesi > > > > P.S. Credit to Larry Garfield for pushing for this better syntax than > > my original idea. > > Credit to Kotlin, from which this syntax is borrowed wholesale. :-) > > I agree the RFC is currently a bit sparse, but that can evolve. I am > strongly in favor of the concept. > > I think the strongest example I have for where it would be useful is the > Drupal database abstraction layer, which I wrote many years ago (with some > input from Karoly, as well). It's evolved a bit since then, but the core > issue is still there. > > For the query builder, many types of queries have WHERE support. > Naturally, we wanted to implement that logic only once, but also didn't > want to use inheritance for that (for all the usual reasons). What we > landed on was a ConditionInterface[1], implemented by a Condition > object[2]. Then each query type class (Select, Insert, Delete, Update, > etc.) would implement ConditionInterface, have an internal $condition > object, and just pass-through all of the methods manually. That was ~100 > lines of boilerplate code on every class. > > Since it was originally written, it looks like someone has factored that > out to a trait[3], which is a bit better in this case since it's reused > enough but it's still ~100 lines of extra boilerplate, just automated. > > Being able to directly delegate to a condition object would have made that > whole process vastly easier, and likely eliminated many uses of > inheritance, too. > > (chx, feel free to quote any of the above in the RFC if it is helpful.) > > However, that example also highlights the main limitation of the current > proposal: Many of the methods are fluent, returning $this. A simple > passthrough method would return the inner object, not the outer object. > Switching it to instead return the outer object (as the trait example > listed does) is something very difficult for the engine to detect > accurately, but in many cases will be the preferred behavior. > > The best solution I can think of for that is allowing the delegation > declaration to specify which methods should be "masked" (for want of a > better term; please suggest one). Something like (spitballing): > > interface I { > public function foo(): self; > public function bar(): self; > public function baz(): string; > } > > class C implements I by $i (mask foo) { > // foo() will return an instance of C, bar() will return whatever > $i->bar() returns (presumably $i), and baz() just returns a string as > normal. > } > > I'm not sure if that is the best approach, but I throw it out to stimulate > discussion. > > [1] > https://git.drupalcode.org/project/drupal/-/blob/main/core/lib/Drupal/Core/Database/Query/ConditionInterface.php > [2] > https://git.drupalcode.org/project/drupal/-/blob/main/core/lib/Drupal/Core/Database/Query/Condition.php > [3] > https://git.drupalcode.org/project/drupal/-/blob/main/core/lib/Drupal/Core/Database/Query/QueryConditionTrait.php Having spent a lot of my PHP career in the Drupal community, I share a lot of the experience Larry does with where this would save tomes of boiler plate. There are a number of other systems that share this sort of finger print where the only reasonable implementation is using a "base class" or traits relaying method calls. Outside of Drupal development, I still find this appealing. There are a lot of cases where extending a class isn't appealing or isn't possible (final, templated, etc) where you want to enhance existing functionality. "Composition over inheritance" as the saying goes. And this seems like a really handy tool for that proxy pattern. Pretty sure I've also run into SO and other discussions around implementing a similar concept with magic __call logic. Laravel found this pattern appealing enough they provide a trait for it[1]. This is technically a bit different, though it is used for this exact solution in Laravel[2]. IMHO in most uses of this would be improved by an interface but the language is standing in the way. > Today, such a change forces the author of the decorator to make a conscious decision. I think this this is a fair point. I've long advocated for Override and inherit[Dd]oc and some other annotations to show this sort of explicit intent. But I currently disagree with the conclusion. I think for the use cases, this has real benefits for existing code. Not without risks but those risks are already being accepted and possibly being replaced by bigger risks in the case of __call usage. I look forward to seeing where this goes and hopefully someday using it. [1] https://github.com/laravel/framework/blob/13.x/src/Illuminate/Support/Traits/ForwardsCalls.php [2] https://github.com/laravel/framework/blob/13.x/src/Illuminate/Http/Client/Promises/FluentPromise.php
