Hi internals,
I'd like to get some feedback on the idea implemented in
https://github.com/php/php-src/pull/5168. It provides an easy way to
implement decorates, by taking care of forwarding any methods you do not
want to override in a type-safe way. See the PR description for details.
I've been playing with this thought for a while, and decided to try this
out now, because typed properties in PHP 7.4 offer a very nice syntax
choice for this feature.
Any initial thoughts?
Regards,
Nikita
I'd like to get some feedback on the idea implemented in
https://github.com/php/php-src/pull/5168. It provides an easy way to
implement decorates, by taking care of forwarding any methods you do not
want to override in a type-safe way. See the PR description for details.
Hi Nikita,
I almost missed this thread because all the discussion has been happening
off-list; for anyone else interested, please note that there's a whole
discussion of the concept, not just the implementation, on the PR.
I actually posted a similar idea a few months ago:
https://externals.io/message/103353
One of the criticisms of my example syntax was that it required you to list
the methods to delegate, which makes multiple delegates easier (you can't
have a name conflict unless you write the name more than once), but large
or frequently-changing targets unwieldy. The idea of using the property's
interface type to generate the whitelist does feel a lot cleaner.
The other criticism though was that it only helps for the methods you're
completely delegating, not those you want to decorate in some way. It would
be nice if there could also be some sugar for those, perhaps a limited form
of AOP where you could intercept the return value of a delegated call?
interface Bar {
public function doSomething(int $a, string $b, Blob $c): string
}
class Foo implements Bar {
public delegate Bar $bar;
after delegate doSomething {
return $return . ' of doom';
}
}
would de-sugar to:
class Foo implements Bar {
public Bar $bar;
public function doSomething(int $a, string $b, Blob $c): string {
$return = $this->bar->doSomething($a, $b, $c);
return $return . ' of doom';
}
}
That would also cover the fluent interface case:
after delegate setFoo {
$this->delegated = $return;
return $this;
}
Decorating what happens before the call would be trickier, because you
need to assign names for the parameters, and by the time you've written out
the whole signature, you might as well implement the method in full.
Regards,
Rowan Tommins
[IMSoP]
That would also cover the fluent interface case:
after delegate setFoo {
$this->delegated = $return;
return $this;
}
I just realised that this could be easily extended to share an
implementation across multiple methods, making a really succinct decorator
definition:
class FluentThingDecorator implements FluentThing {
private int $setterCount=0;
private int $getterCount =0;
private delegate FluentThing $delegated;
public function __construct(FluentThing $delegate) {
$this->delegated = $delegate;
}
public function getSetterCount() {
return $this->setterCount;
}
public function getGetterCount() {
return $this->getterCount;
}
after delegate getFoo, getBar, getBaz {
$this->getterCount++;
return $return;
}
after delegate setFoo, setBar, setBaz {
$this->setterCount++;
$this->delegated = $return;
return $this;
}
}
It might be useful to have access to the name of the function actually
called, e.g. for an audit log decorator; maybe the de-sugaring could happen
early enough that METHOD took on the right value for each case.
Regards,
Rowan Tommins
[IMSoP]