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