On Thu, Apr 21, 2016 at 11:52 PM, Larry Garfield <la...@garfieldtech.com> wrote:
> 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: > > <<test($a + $b > 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. > They don't exist in any scope. You get an AST of this expression, what you do with it is entirely your choosing and you can set the scope/context yourself. > > 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: > > <<One, Two>> > function foo() {} > > <<One>> > <<Two>> > 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: > > > <<AThing>> > <<AnotherThing('stuff')>> > <<MoreThing(1, 2, 3)>> > 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. > I like that its just arrays, PHP is not exclusively OOP, so making this an OOP feature doesn't make sense to me. A library like Doctrine can always add the meaning on top, for example checking: <<ORM\Entity>> class User { } Again resolving this against namespaces for example inside Doctrine Annotations itself, like its done right now. > > 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: > This is why it should be just arrays, using classes suddenly tons of special things can happen and must be thought of. > > <<AThing(a => '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. You can build any kind of structure on top of $refl->getAttributes() inside your framework (Drupal in this case) > > > -- > --Larry Garfield > > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >