Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:92612 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 19224 invoked from network); 21 Apr 2016 21:52:08 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 21 Apr 2016 21:52:08 -0000 Authentication-Results: pb1.pair.com smtp.mail=larry@garfieldtech.com; spf=permerror; sender-id=unknown Authentication-Results: pb1.pair.com header.from=larry@garfieldtech.com; sender-id=unknown Received-SPF: error (pb1.pair.com: domain garfieldtech.com from 66.111.4.26 cause and error) X-PHP-List-Original-Sender: larry@garfieldtech.com X-Host-Fingerprint: 66.111.4.26 out2-smtp.messagingengine.com Received: from [66.111.4.26] ([66.111.4.26:53148] helo=out2-smtp.messagingengine.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 7F/B7-14036-88B49175 for ; Thu, 21 Apr 2016 17:52:08 -0400 Received: from compute4.internal (compute4.nyi.internal [10.202.2.44]) by mailout.nyi.internal (Postfix) with ESMTP id B957E2066C for ; Thu, 21 Apr 2016 17:52:05 -0400 (EDT) Received: from frontend2 ([10.202.2.161]) by compute4.internal (MEProxy); Thu, 21 Apr 2016 17:52:05 -0400 DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d= messagingengine.com; h=content-transfer-encoding:content-type :date:from:in-reply-to:message-id:mime-version:references :subject:to:x-sasl-enc:x-sasl-enc; s=smtpout; bh=vDseC4rqOqjOJjb Vj+bxvq/sOso=; b=g0Au460yjj98w5WahdFCZb5/Em3MMO+6a61ODQ903LIskQC fPGlguaTzALloMhaP3YE7YSXTJ7nqCeif5KsCzJscbjhgWzN6I5eTWvghIahN17O VaylMdlxA/XIui7Kj8G1uEYUvBeTQDMvJjdTFswx8xTX1x0UvR+xVbrD+pRk= X-Sasl-enc: ylrkBCdQcbniQpCrfsN0UOx397E2zMIbSx2dBXBxFDQX 1461275525 Received: from Crells-MacBook-Pro.local (unknown [63.250.249.138]) by mail.messagingengine.com (Postfix) with ESMTPA id 6CC686800E1 for ; Thu, 21 Apr 2016 17:52:05 -0400 (EDT) To: internals@lists.php.net References: Message-ID: <57194B85.60507@garfieldtech.com> Date: Thu, 21 Apr 2016 16:52:05 -0500 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:38.0) Gecko/20100101 Thunderbird/38.0.1 MIME-Version: 1.0 In-Reply-To: Content-Type: text/plain; charset=windows-1252; format=flowed Content-Transfer-Encoding: 7bit Subject: Re: [PHP-DEV] [RFC] PHP Attributes From: larry@garfieldtech.com (Larry Garfield) On 4/21/16 4:13 PM, Dmitry Stogov 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. Thanks, Dmitry! In concept I am in favor of syntax-native annotations, although I have some concerns with the specifics of the proposal. Thoughts in no particular order: First, for the getAttributes() reflection method, please oh please don't return array-or-false. That's horrible. Just return an empty array if there aren't any, as that makes getAttributes() entirely type safe and saves all callers from a mandatory if-check. (See http://www.garfieldtech.com/blog/empty-return-values for more information.) The reflection section further indicates that the type of the result is variable, which means I cannot know in advance if I'm going to get back a scalar or an array. If we go with this free-form approach, I'd honestly prefer to always get back an array, even for single value, so that I can always know the type I'm dealing with. (Since I cannot enforce a given attribute to be single-value.) For the expression example: < 0)>> function foo($a, $b) { } It is not at all clear to me what scope the annotation's $a and $b exist in. Are the they same $a and $b as in the function signature? If so, what happens if I reflect the function before ever calling it? How can I evaluate test? Or are they inherited from the global scope at the time of declaration? (That scares me a great deal.) I don't know what to make of that at all. In the "Attribute syntax" section, the text says the tokens are the left and right double-angle character, as used for quotations in some European languages. The rest of the text says it's two left/right carrot characters, as seen above the comma and period on US keyboards. I'm assuming the former is just a typo/auto-correct bug. If I read correctly, the following two would be semantically identical: <> function foo() {} <> <> function foo() {} Is there a reason you chose the name "attribute" rather than "annotations", which seems at least in PHP to be the more common term for this type of declaration? It appears that the annotations themselves are entirely free-form. At the risk of expanding the typing debate, this concerns me as then all we're adding is a new way to parse undocumented, undefined anonymous structs. How can I say what annotations mean what for my ORM, or routing system, or whatever? We're back to, essentially, out-of-band documentation of big anonymous structs (aka associative arrays). A more robust alternative would be something along the same lines that Doctrine uses: Make annotations actual classes. To wit: <> <> <> function foo($a, $b) { } Where AThing, AnotherThing, and MoreThing are defined classes, and subject to namespaces and use statements. Then what gets returned from getAttributes() is an array consisting of an instance of AThing, an instance of AnotherThing, and an instance of MoreThing. In this example we'd just call their constructors with the listed values and let them do as they will. Doctrine uses named properties in the annotation that maps to properties on the object, which is even more flexible and self-documenting although I don't know how feasible that is without opening up the named properties can of worms globally. Either way, the advantage then is that I know what annotations are available, and the class itself serves as documentation for what it is, what it does, and what its options are. It also helps address collisions if two different libraries both want to use the same keyword; we already have a class name resolution mechanism that works and everyone is familiar with. One concern is that not all classes necessarily make sense as an annotation; perhaps only classes with a certain interface can be used. Actually (thinking aloud here), that would be a possible solution to the named property issue. To wit: < 'a', b => 'b')>> foo() {} class AThing implements Attribute { public static function attributeCreate(array $params) { return new static($param['a'], $param['b']); } } $r = new ReflectionFunction('foo'); $a = $r->getAttributes(); $a is now an array of one element, an instance of AThing, created with 'a' and 'b'. The specifics here are probably terrible, but the general idea of using classes to define annotations is, I think, a big step forward for documentation and avoiding multi-library collisions. While I know some of the things Drupal 8 is using annotations for are arguably excessive (and I would agree with that argument in some cases), as is I fear the proposed system is too free-form and rudimentary for Drupal to switch to them. -- --Larry Garfield