# [Pre RFC] Automated delegation using implements by

- Thread: https://externals.io/message/132737
- Original archive: https://news-web.php.net/php.internals/132737
- Started by Karoly Negyesi on 2026-10-01T00:29:56+00:00
- Messages: 6
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #132737 — Karoly Negyesi — 2026-10-01T00:29:56+00:00

URL: https://externals.io/message/132737#132737

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.

---

## #132738 — Calvin Buckley — 2026-10-01T03:55:14+00:00

URL: https://externals.io/message/132737#132738 · In reply to: #132737

> 
> 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.
> 

The RFC itself needs to be posted to wiki.php.net (and link to your
implementation PR). See below link for the process there and how to
get access to edit:

https://wiki.php.net/rfc/howto

---

## #132747 — Sebastian Bergmann — 2026-10-01T13:03:22+00:00

URL: https://externals.io/message/132737#132747 · In reply to: #132737

Am 01.10.2026 um 02:29 schrieb Karoly Negyesi:
> I submitted a draft RFC to https://github.com/php/php-src/pull/24030 
> <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 RFC motivates this feature with "unnecessary boilerplate and 
maintenance burden as the interface gets more methods over time". I think 
this premise is flawed: an interface that keeps gaining methods over time 
usually violates the Interface Segregation Principle. The remedy for that 
is smaller, more focused interfaces, not language support that makes 
delegating to ever-growing interfaces cheaper.

There is also a correctness concern: with generated forwarding, a method 
that is later added to the interface is silently delegated by every class 
that uses "implements ... by". In the RFC's own example, a new 
URL-generating method would be forwarded without the metadata bubbling 
that MetadataBubblingUrlGenerator exists for. Today, such a change forces 
the author of the decorator to make a conscious decision.

I am also not convinced that any benefit this may have outweighs its 
downsides: it adds new syntax that static analysis tools need to learn 
about, and it adds complexity to the compiler. The "To the Ecosystem" 
section currently only lists "Less boilerplate"; it should account for 
these costs as well.

-- 
Sebastian Bergmann                                 https://phpunit.expert

Stay up to date with PHPUnit: https://phpunit.expert/newsletter

---

## #132750 — Larry Garfield — 2026-10-01T13:49:38+00:00

URL: https://externals.io/message/132737#132750 · In reply to: #132737

> 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

---

## #132758 — James Gilliland — 2026-10-01T16:29:55+00:00

URL: https://externals.io/message/132737#132758 · In reply to: #132750

On Thu, Oct 1, 2026 at 8:52 AM Larry Garfield <larry@garfieldtech.com>
wrote:

> 
> > […]
>
> 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

---

## #132767 — Rowan Tommins [IMSoP] — 2026-10-01T21:21:05+00:00

URL: https://externals.io/message/132737#132767 · In reply to: #132750

Hi all,


>However, that example also highlights the main limitation of the current proposal: Many of the methods are fluent, returning $this.

This is where I've always stumbled when trying to come up with similar feature proposals - when you start looking for use cases, a lot of them need *just one small thing* on top of the forwarded call. So you add an extra keyword here, and a special logic there, and eventually it no longer feels like an elegant shorthand at all.

Looking at the Laravel FluentPromise example James shared, I'm not sure if it's quite the same handling of $this that you're describing; so would we need multiple different keywords to describe exactly how the return value should be manipulated?

I've written decorator classes that exist only to catch exceptions from the real method and log them to a specific channel. Most of the code is boilerplate passing along the arguments and return values. I can't just say "delegate all these methods" at the class level, but I'd quite like some inline syntax for "forward call to $this->wrapped".

But then again, there are cases where you want to forward *all except one* argument, or forward all arguments but add an extra flag, and so it goes on.

The best I can think of is something inspired by Aspect Oriented Programming: forward these calls, but decorate them with these pre- and post-actions. But I've not managed anything that doesn't look like an obfuscated version of the original code.

Regards,

Rowan Tommins
[IMSoP]
