Hi Ben,
I have been looking at your #[Deprecated] PR to get an idea of where to
start with implementing this, I think Peter's suggestion of how it might
look syntactically is also interesting - it's exactly that sort of question
of how would you imagine devs implementing and using decorators in PHP that
I wanted to get a feel for before I started trying to build it or draft an
RFC. Your other tips regarding zend_observer and zend_call_function are
much appreciated, saves me a lot of digging through the source figuring out
the appropriate APIs for myself.

Before and after hooks are a similar but slightly different implementation,
semantically, to what I was originally suggesting but may be the preferred
option. I think to be useful, you probably still need some way to choose
not to call the original, decorated function - but I guess you could just
throw an exception in your before hook and that might be sufficient control
in practice.

Regards,
David

On Sun, Mar 14, 2021 at 11:52 AM Benjamin Eberlei <kont...@beberlei.de>
wrote:

>
>
> On Sat, Mar 13, 2021 at 5:51 PM David Gebler <davidgeb...@gmail.com>
> wrote:
>
>> With the introduction of attributes in PHP 8, this new behaviour is still
>> quite sparsely documented. Some of the articles I've seen out there,
>> though, liken PHP's attributes to similar constructs in other languages
>> including decorators in Python.
>>
>> Attributes are not the same thing as (Python's concept of) decorators and
>> they shouldn't be confused; a decorator is a function which wraps another
>> function and is automatically called in place of the wrapped function.
>>
>> This isn't currently possible in PHP. Using frameworks like Symfony, we
>> can
>> start to build things like this:
>>
>> class UserProfileController {
>>     #[LoginRequired]
>>     public function editProfile(...) { }
>> }
>>
>> but the logic of enforcing our "require the user to be logged in"
>> decorator
>> relies on the surrounding framework controlling the flow of execution,
>> reading the attribute and deciding whether to call the decorated method
>> editProfile() at all.
>>
>> What we *can't* do is something like this:
>>
>> class Foo {
>>     private function timer(callable $wrapped)
>>     {
>>         $start = microtime(true);
>>         $wrapped();
>>         $end = microtime(true);
>>         $total = $end - $start;
>>         echo "Executed function in $total second(s)\n";
>>     }
>>
>>      #[timer]
>>     public function bar($a, $b) { ... }
>>
>>     #[timer]
>>     public function baz($a, $b) { ... }
>> }
>>
>> What I'm wondering is whether there's a desire / interest for a built-in
>> attribute to provide this kind of behaviour modification.
>>
>> I'm thinking something like
>>
>> class Foo {
>>     private function timer(callable $wrapped) { ... }
>>
>>     #[Decorator([self::class, 'timer'])]
>>     public function bar() {
>>         echo "Bar";
>>     }
>> }
>>
>> Where this would result in any call to $foo->bar() being equivalent to as
>> if the above were defined as:
>>
>> class Foo {
>>     private function timer(callable $wrapped) { ... }
>>
>>      public function __bar() {
>>         echo "Bar";
>>      }
>>
>>     public function bar() {
>>         return $this->timer([$this, '__bar']);
>>     }
>> }
>>
>> I'm not saying I have the skills to implement this attribute (though I'd
>> happily try), I'm not even in a position to propose a draft RFC at this
>> stage, just throwing the idea out there to get a feel for what people
>> think
>> of the concept?
>>
>
> In my opinion it would be a fantastic addition to Core to be used by
> application frameworks with "hook philosophies" that hack this
> functionality on top of PHP with code generation or event dispatchers at
> the moment (Magento 2, Drupal, Neos, Wordpress and so on) makes this a
> potential future with wide adoption. If you'd get 2/3 votes for it is
> another topic.
>
> However, as functionality it could be provided as an extension first for a
> proof of concept. The ingredients are all there, it doesn't need to be in
> core:
>
> 1. Register an internal attribute, see my #[Deprecated] PR as an example
> https://github.com/php/php-src/pull/6521
>
> 2. Register a zend_observer as a first step, that detects
> functions/methods with a new #[Intercept] or whatever attribute you want
> and registers observer callbacks. See ext/zend_test
> https://github.com/php/php-src/blob/master/ext/zend_test/test.c or
> tideways/php-xhprof-extension:
> https://github.com/tideways/php-xhprof-extension/blob/master/tideways_xhprof.c#L30-L57
>
> 3. Use C API zend_call_function in the observer to call your interceptor.
>
> Cobbling this together as a Frankestein monster from existing code should
> be achievable even if you haven't worked with PHP core yet imho. This could
> replace the php-aop extension that isn't maintained anymore.
>
> Using a zend_obserer would only allow you to register a before and after
> hook, the alternative with a "$wrapped()" would be significantly more
> complex with the existing building blocks.
>
> Hooking into zend_exeute_ex would allow you to implement around handling,
> but at this point is not recommended anymore, because its incompatible with
> JIT and might be removed in the future.
>
>
>>
>> Regards,
>> Dave
>>
>

Reply via email to