Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:92730 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 40094 invoked from network); 25 Apr 2016 14:12:11 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 25 Apr 2016 14:12:11 -0000 Authentication-Results: pb1.pair.com smtp.mail=guilhermeblanco@gmail.com; spf=pass; sender-id=pass Authentication-Results: pb1.pair.com header.from=guilhermeblanco@gmail.com; sender-id=pass Received-SPF: pass (pb1.pair.com: domain gmail.com designates 209.85.213.174 as permitted sender) X-PHP-List-Original-Sender: guilhermeblanco@gmail.com X-Host-Fingerprint: 209.85.213.174 mail-ig0-f174.google.com Received: from [209.85.213.174] ([209.85.213.174:38150] helo=mail-ig0-f174.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 5F/17-00233-9B52E175 for ; Mon, 25 Apr 2016 10:12:10 -0400 Received: by mail-ig0-f174.google.com with SMTP id m9so64426539ige.1 for ; Mon, 25 Apr 2016 07:12:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc; bh=mpFbedOIBmyaePu7p3IdAWkMU2NGjSY3bdM8gCINego=; b=w4UM6QzWKdbKHxkOF1Ldl/MStIKga3kvrpUuCEFaMAUgZe5uXy1vKwhUTBopwo5BvU l6TVXXJoNRtLGdfN51Ap8iqz1R3ANcEa6x4HFIaTwG+MpBHvL2ehVo43sOmwV8fIz1s0 WBTpL2EkMNnY7gCT4CAT2t1OeSyRI5xJP1p1/EwterbiWsHkddOj6j0G8VQ1+U5hPlS1 TikizEmzHbBbpvm/RXnoK2LUIzItxfu723LTi/klvkeRUy8HTasY0tI0XME2tN7zVBT9 WB//bjr3QXHFJMX/mA2gNSsukP3Q5QGzdLgXYFMD124nZ8yBg/Ome1VI+gAYCxSExAKg wmKg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to:cc; bh=mpFbedOIBmyaePu7p3IdAWkMU2NGjSY3bdM8gCINego=; b=DgjH9WUN4NqZueZEdLrgBQrrKfH2/h/N4YNAMzos5xioCxYBS2Cck/DsYVoCSBWS75 FjrSJ3LhEMMqe7ydJDpeQSjExLM3wd/PS6MzKMohJ0022ALXGavDkEyZGSTUYNgTOyO+ Zq8Vtis3lGIkvpNSUWBJv5ja7mPciKfzS/qE4MmoykmaP+oszqYdhC9zJKxzgAIWWBf5 pCP37l5PKlxTL7AB2wO5dlV3zLXu7deoaPrJQIHAsuJXFTG1uP0uWbAPcXys7gK6kB9L o3QhzlkIzOqK75pRZtlEXad5P/qmagMXUQyRrGfZzrcXfIZ/OuAaUwpWQRWHdmBSLsVt ncSQ== X-Gm-Message-State: AOPr4FWfb5+lgb136JE2/hRuYrK8ePDpb3j8itA4DJkr9vb+LZ+vuY47OinUr6YkNdRB28mROUWFkyXhPrtCPw== X-Received: by 10.50.123.132 with SMTP id ma4mr12898239igb.92.1461593526839; Mon, 25 Apr 2016 07:12:06 -0700 (PDT) MIME-Version: 1.0 Received: by 10.50.85.10 with HTTP; Mon, 25 Apr 2016 07:11:46 -0700 (PDT) In-Reply-To: <571DCA6A.2070803@zend.com> References: <571965D1.9020102@zend.com> <5719CDB2.90103@zend.com> <571DCA6A.2070803@zend.com> Date: Mon, 25 Apr 2016 10:11:46 -0400 Message-ID: To: Dmitry Stogov Cc: Dominic Grostate , PHP internals Content-Type: multipart/alternative; boundary=001a113603bef9300805314fc2ba Subject: Re: [PHP-DEV] [RFC] PHP Attributes From: guilhermeblanco@gmail.com ("guilhermeblanco@gmail.com") --001a113603bef9300805314fc2ba Content-Type: text/plain; charset=UTF-8 On Mon, Apr 25, 2016 at 3:42 AM, Dmitry Stogov wrote: > > > On 04/22/2016 06:39 PM, guilhermeblanco@gmail.com wrote: > > > On Fri, Apr 22, 2016 at 3:07 AM, Dmitry Stogov wrote: > >> >> >> On 04/22/2016 04:05 AM, >> guilhermeblanco@gmail.com wrote: >> >> Hi Dmitry, >> >> As a previous suggester of metadata information built-in into PHP, and >> also one of developers of the most used metadata library written in PHP, I >> understand this feature implementation requires several design decisions >> and also a good understanding of specific situations users may require. >> >> While I am a strong supporter of a more robust solution, this is already >> a good start. >> A few things I'd like to ask for my own understanding and also >> suggestions too: >> >> 1- I understand you took a minimalistic approach towards a "dumb" >> implementation for attributes (when I mean "dumb", the idea here is towards >> a non-OO approach). Can you explain your motivations towards this approach? >> >> I see two distinct approaches of implementation for this feature. Both of >> them have some common demands, like lazy initialization of metadata. Here >> they are: >> >> - Simplistic approach, which lets consumers of the feature do all the >> work related to validation, assertion of valid keys, values, etc >> This does not invalidate the ability to leverage of some features that a >> more robust implementation demands. >> >> - Robust approach: language takes the burden of instantiating complex >> structures, validating, assertion of valid keys, values, if this complex >> structure is allowed to be instantiated in that given class, method, etc. >> >> >> I didn't exactly understand what do you suggest. >> If you are talking about Attribute objects initialization during >> compilation - this is just not possible from implementation point of view. >> Now attributes may be stored in opcache SHM and relive request boundary. >> Objects can't relive requests. >> > > > I know that object instances are not cross-requests. Explicitly, I > mentioned that both approaches require lazy-initialization (which means, > whenever you call getAttributes() or getAttribute()). > > What I mentioning is that your approach is basically a new key/value > syntax that are used specifically for Attributes. We could easily turn this > into a more robust approach if instead of defining key/value pairs, we > instantiate objects or call functions. You already demonstrated interest to > support <> reusing the imports (which is our biggest headache > in Doctrine Annotations), so why not issue constructor or function calls > there? That would simplify the work needed for consumers and also add room > for later improvements. > > So basically in this example: > > use Doctrine\ORM; > > <> > class User {} > > $reflClass = new \ReflectionClass("User"); > var_dump($reflClass->getAttributes()); > > We'd be changing from this: > > array(1) { > ["Doctrine\ORM\Entity"]=> > array(1) { > [0]=> > string(4) "user" > } > } > > Into this: > > array(1) { > ["Doctrine\ORM\Entity"]=> > object(Doctrine\ORM\Entity)#1 (1) { > ["tableName"]=> > string(4) "user" > } > } > > > As I showed already, it's very easy to do this transformation at higher > layer. > > $reflClass = new \ReflectionClass("User"); > $attributes = $reflClass->getAttributes() > foreach ($attributes as $key => &$val) { > $val = new $key(...$val); > } > var_dump($attributes); > > Construction objects directly in Reflection*::getAttributes() method, > doesn't make significant benefits and even makes limitation. > Sorry, but I don't see how limitations are added. If you call a function, static method or constructor, you actually add whole new level of possibilities, and I fail to see which limitations are added. Could you provide me one? Calling the function/constructor/static method, not only helps to better segregate userland code, but it also adds subsequents extensibility. I can highlight examples: - Support for Inheritance and overrides, through @Inherit, @Override, etc. While you might not see how it could be used now, other developers might be weirdly creative. - Targeting of annotations, such as limiting its scope to be only class, method or property. We use this extensively in Doctrine, where you cannot define Doctrine\ODM\Entity over a property. - Separating what can be considered as an annotation and what cannot. Built-in @Annotation as a marker would differentiate that I can do call Doctrine\ORM\Entity and not Doctrine\ORM\UnitOfWork. - Make it easier to support an AOP extension, where it could detect annotations being used and override DO_FCALL to call before, after or around through the implementation of interfaces. - If we ever decide to support named parameters, taking advantage of that would become natural, like: < "user")>> > > > > >> >> 1- Your approach is basically defining an array. Could you explain your >> line of thinking on why you didn't consider a syntax like the one below? >> >> <["key" => "value"]> >> class Foo {} >> >> I didn't try to invite new syntax. Just completely took it from HHVM. >> > > My idea was based on your current proposal, which is basically a way to > define key/value pairs. > If you decide to go minimalistic, that is probably my best line of > thinking. > > >> >> >> >> 2- I see that you added support over functions, classes, constants and >> properties. According to the RFC, getAttributes() was added over >> ReflectionFunction. Is there a reason why support was not added to methods >> (ReflectionMethod extends ReflectionFunctionAbstract, which was not >> mentioned on RFC)? Any reason to not support it in function/method >> parameters? >> >> ReflectionMethod is a child of ReflectinFunction, so it's supported. >> > Attributes are allowed for the same entities as doc-comments (they are not >> allowed for parameters) >> > > I was asking if there was a purpose to not support Attributes over > ReflectionParameter. Example: > > class Foo { > public function bar(<> Bar $bar) : bool { > // ... > } > } > > $reflClass = new \ReflectionClas("Foo"); > $reflMethod = $reflClass->getMethod("bar"); > $reflParameter = $reflMethod->getParameters()[0]; > > var_dump($reflParameter->getAttributes()); > > > I understood, we may add this ability later. > I'd say we should add this from day one. A quick use case that comes to my mind are parameters conversion that happens in Symfony2 through their "extra" bundle (doc: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html ). In a controller action (it's a class method), you have the ability to convert the Request object into something else that makes more sense for you. Example: class UserController extends Controller { public function viewAction(<> User $user = null) { if ($user === null) { throw new NotFoundException("User not found"); } return ["me" => $this->getUser(), "user" => $user]; } } > > > >> >> >> >> 3- Did you put any thought on inheritance? What I mentioned in comment #1 >> is even smaller than what you implemented in RFC. >> Assuming you keep the RFC approach, did you consider support overrides, >> inherit, etc? >> >> >> In my opinion, attributes don't have to be inherited. >> If you think differently - please explain your point. >> > > Of source I can. > A simple case would be to increate visibility of the inherited property. > It was declared in a parent class as protected, but now you want public, > and you still want to keep all parent defined Attributes. > > Very questionable. If you redefine property, it shouldn't inherit > attributes. > This leads to some serious copy/paste, highly error prone... =( > > > Another example is like we do in Doctrine. We support a callback system > which we named as lifetime callbacks. Pre-persist is one of them, which is > called every time a given Entity is about to be persisted into DB. When > you're dealing with inheritance, you can potentially override the method > content and you still want to trigger the same operation as if it was > untouched. Example: > > use Doctrine\ORM; > > trait Timestampable { > protected $created; > protected $updated; > > <> > public function prePersist() { > $this->created = $this->updated = new \DateTime("now"); > } > > <> > public function preUpdate() { > $this->updated = new \DateTime("now"); > } > } > > <> > class User { > use Timestampable; > > public function prePersist() { > // Add my custom logic > } > } > > The implication is that through a simplistic approach, inheriting (or > overriding) is not clear and I can't figure it out an easy way to achieve > that. > Now if we go towards calling a function or class constructor like I > mentioned before, then we could easily build structures like __Inherit, > __Override, etc. > > It's definitely, not clear when attribute inheritance make sense and when > completely not. For example, if we mark some method to be JIT-ed, it > doesn't mean that we like to JIT methods of all children. So, I prefer not > to do inheritance at all. The higher layers may emulate "inheritance" of > some attributes their selves (like you do this with doc-comments). > As I said earlier, if you do a call based approach, we could create @Inherit or @Override, which would not only make us safe from support, but also gives more power to developers. > > > > >> >> >> 4- I understand that a more robust attribute solution would be required >> to achieve this, but one of the biggest advantages of AOP is the ability to >> perform custom logic before, after or around... However, I don't know if >> any kind of triggers came in your head or are planned as a future RFC. >> Let me highlight one example: Every time a class, property or method is >> called that is annotated as <>, I would like to issue an >> E_USER_DEPRECATED warning. A trigger-like solution would be required. Did >> this concept came to your mind? >> >> This is not a subject of this RFC. >> Attributes provides a storage for metadata, but don't define how to use >> them. >> Especially, for your use-case: >> 1) it's possible to create preprocessor that embeds corresponding >> trigger_error() call >> 2) it's possible to write a PHP extension that plugs-into compiler chain >> and checks <> attribute for each compiles function, then sets >> ZEND_ACC_DEPRECATED flag >> 3) It's also possible to override DO_FCALL opcodes and perform checks >> there (this is inefficient) >> >> > With this simplistic approach, I agree there's 0 value into considering > this. > However, taking a more robust approach would potentially open this > possibility through a simpler extension. > > > You saw, Sara named even this proposed solution a bit over-designed. > it make no sense to implement all functionality at language level. > Actually, keeping simple base interface, opens doors for more use-cases. > > Thanks. Dmitry. > > > > > >> Thanks. Dmitry. >> >> >> >> >> >> Regards, >> >> On Thu, Apr 21, 2016 at 7:44 PM, Dmitry Stogov < >> dmitry@zend.com> wrote: >> >>> >>> >>> On 04/22/2016 02:16 AM, Dominic Grostate wrote: >>> >>>> >>>> This is amazing. It would actually allow us to implement our automated >>>> assertions ourselves, as opposed to requiring it within the language. >>>> >>>> this was the idea - to give a good tool instead of implementing every >>> possible use-case in the language. >>> >>> Could it also support references? >>>> >>>> <> >>>> function foo($a) { >>>> >>>> } >>>> >>>> yes. "&$a" is a valid PHP expression. >>> >>> If you plan to use this, I would appreciate, if you to build the patched >>> PHP and try it. >>> The early we find problems the better feature we will get at the end. >>> >>> Thanks. Dmitry. >>> >>> >>> On 21 Apr 2016 10:13 p.m., "Dmitry Stogov" >>> dmitry@zend.com>> wrote: >>>> >>>> Hi, >>>> >>>> >>>> I would like to present an RFC proposing support for native >>>> annotation. >>>> >>>> The naming, syntax and behavior are mostly influenced by HHVM >>>> Hack, but not exactly the same. >>>> >>>> The most interesting difference is an ability to use arbitrary PHP >>>> expressions as attribute values. >>>> >>>> These expressions are not evaluated, but stored as Abstract Syntax >>>> Trees, and later may be accessed (node by node) in PHP extensions, >>>> preprocessors and PHP scripts their selves. I think this ability >>>> may be useful for "Design By Contract", other formal verification >>>> systems, Aspect Oriented Programming, etc >>>> >>>> >>>> https://wiki.php.net/rfc/attributes >>>> >>>> >>>> Note that this approach is going to be native, in contrast to >>>> doc-comment approach that uses not well defined syntax, and even >>>> not parsed by PHP itself. >>>> >>>> >>>> Additional ideas, endorsement and criticism are welcome. >>>> >>>> >>>> Thanks. Dmitry. >>>> >>>> >>> >> >> >> -- >> Guilherme Blanco >> Lead Architect at E-Block >> >> >> > > > -- > Guilherme Blanco > Lead Architect at E-Block > > > -- Guilherme Blanco Lead Architect at E-Block --001a113603bef9300805314fc2ba--