Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:113512 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 32556 invoked from network); 14 Mar 2021 11:59:14 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 14 Mar 2021 11:59:14 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id BC87D1804B8 for ; Sun, 14 Mar 2021 04:52:26 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2, SPF_HELO_NONE,SPF_NONE autolearn=no autolearn_force=no version=3.4.2 X-Spam-Virus: No X-Envelope-From: Received: from mail-wm1-f51.google.com (mail-wm1-f51.google.com [209.85.128.51]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Sun, 14 Mar 2021 04:52:25 -0700 (PDT) Received: by mail-wm1-f51.google.com with SMTP id b2-20020a7bc2420000b029010be1081172so17768812wmj.1 for ; Sun, 14 Mar 2021 04:52:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=beberlei-de.20150623.gappssmtp.com; s=20150623; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=h9KCWUBPuYhvkQPrAnCEMhz9c89KrfqTZxqxqwb+8SM=; b=bWSh+gbNTZg9qssHyNeHuCZYazXkTiW7fLirbUPNYO8KXtGjYdlJAxqV7zzDRuVjU3 fUXw5uc/FLeH6L35YtFpbx9Lb2T4TrHUyp+dFJKwZvUeAoXL5wNOMM3C3oFR03/6rOj2 Rw3+7lJFr3uiemnw3UZ5kZ9Njwe48hv0O1ya5FlQovF5bgAqPXs9H/gVpmGaTse3dCvE UnRiEP4VSHN9ZKSkm+MxR6PGa2ln6v7eQorkCyFinugBb8S8jrL9NwSls1KtFyTH3FR4 o+qr4i4YteiMm4PGI1JK5ChJk2ib/zhyyCEHQQl5wyYorsUwTZODarWlkwWmkew6WbST 7KmA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=h9KCWUBPuYhvkQPrAnCEMhz9c89KrfqTZxqxqwb+8SM=; b=qiJndSlwPl6hxcYMtUfY4qOd/xwLyFj/JLCN7kEDSL4lacmhzDbBkS3apqHuYwG5Av L6qV3fZ5dYrlS1U19r0Fx8MmTZy9kBYcNfevjWUW/WzsD+9Y1fg8rgD7qLVpIslfUgLf aGOInwVcWIwhpPEKq4T533mZLFjUbkaCcit8+TmLHrZOzNrjh3UGKbTTJ9GbufR+2oGc GBrzfdaJHGtjiCaHHsIBHM4oCLMGBuRjE4ZjQbgKwG2hyai+bcpiHBZavM/4N94VY4nT 2X250Fa/IHbhfshwnqUqE3otyfsdxm0LrPKo4qSxGGTwMG44xTBwvCaNeuZoUTWZr4bm noIw== X-Gm-Message-State: AOAM53074kMInEHYGHu21mM7FemEZn6u8seTQNL/LRrO3vnQhKoul+Xe Dj0Tr/AbZsIDUepp3QZByLonZWsuFozo+GM+qdLGqA== X-Google-Smtp-Source: ABdhPJzoBo20mIrTC7zffLbDv3gzJbqPSa9j0+168ADe6jY8HX2fAFZcRX3vXUf68VCdTFXV/XZX0uy9vPSguIEdnE0= X-Received: by 2002:a7b:ce06:: with SMTP id m6mr21021001wmc.38.1615722742513; Sun, 14 Mar 2021 04:52:22 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: Date: Sun, 14 Mar 2021 12:52:11 +0100 Message-ID: To: David Gebler Cc: PHP internals Content-Type: multipart/alternative; boundary="0000000000001f923a05bd7dc470" Subject: Re: [PHP-DEV] Built-in decorator attribute? From: kontakt@beberlei.de (Benjamin Eberlei) --0000000000001f923a05bd7dc470 Content-Type: text/plain; charset="UTF-8" On Sat, Mar 13, 2021 at 5:51 PM David Gebler 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 > --0000000000001f923a05bd7dc470--