On Sun, Mar 14, 2021, at 7:33 AM, David Gebler wrote:
> 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

I've been toying (mostly mentally) with the idea of using attributes for guard 
rules on object properties and function parameters.  Benjamin gave me basically 
the same answer. :-)  It sounds like both that and AOP-attributes reduce to the 
same core problem: Allow an attribute to specify a caller that intercepts an 
action being taken (call method, write to property, hell maybe even read 
property although that seems less useful).

So, what's the minimal surface area needed in C (either core or an extension) 
to allow the rest of that to be done in user space?  

--Larry Garfield

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php

Reply via email to