[RFC] PHP Attributes

php.internals

Dmitry Stogov

10 years ago
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.

Larry Garfield

10 years ago
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. 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. 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: <<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.
-- --Larry Garfield

Benjamin Eberlei

10 years ago
On Thu, Apr 21, 2016 at 11:52 PM, Larry Garfield <larry@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)

Dmitry Stogov

10 years ago
On 04/22/2016 12:52 AM, Larry Garfield 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.)
Makes sense. I may change this.
> > 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.)
I'm not sure yet. both decisions may make sense. If I expect just a single value, I'll have to check the number of elements (or just ignore values above the first).
> > 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?
This is just an AST. It may contain any valid PHP expression syntax, but variable, functions and constants don't have to be valid.
> How can I evaluate test?
I hope this functionality will be provided by php-ast extension. Currently, it is not a problem to reconstruct PHP source from AST and then use regular eval(). In general, we may find a more efficient way.
> 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.
AST is going to be mainly used by extension and pre-processors (like AOT and DBC), but in general, they also may be used directly in scripts. <<test($a + $b > 0)>> function foo($a, $b) { ast_eval(RefelectionFunction(__FUNCTION__)->getAttributes()["test"]); }
> DB > 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.
yeah, computers think they are too smart :)
> > If I read correctly, the following two would be semantically identical: > > <<One, Two>> > function foo() {} > > <<One>> > <<Two>> > function foo() {}
right
> > 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?
I took the name from HHVM. Personally, I don't care about naming at all.
> > > It appears that the annotations themselves are entirely free-form.
no. they are parsed according to PHP expression syntax rules. syntax mistakes in attributes are going to be caught at compile time.
> 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.
This is just a next level. Attributes are just a storage of meta-data. You may use them as you like :) function getAttributesAsObjects($r) { $ret = array(); $a = $r->getAttributes(); foreach ($a as $name => $val) { $ret[] = new $name(...$val); } return $ret; }
> > 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: > > <<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. >
We can't use classes and object in storage directly, because they may be defined in different PHP script, may be changed between requests, etc. but as I showed, it's very easy to construct corresponding objects on request. Thanks for deep review and good suggestions. Dmitry.

Dominic Grostate

10 years ago
This is amazing. It would actually allow us to implement our automated assertions ourselves, as opposed to requiring it within the language. Could it also support references? <<sanitize(&$a)>> function foo($a) { } On 21 Apr 2016 10:13 p.m., "Dmitry Stogov" <dmitry@zend.com> wrote:

Dmitry Stogov

10 years ago
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? > > <<sanitize(&$a)>> > 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.

Guilherme Blanco

10 years ago
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. 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 {} 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? 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? 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 <<deprecated>>, I would like to issue an E_USER_DEPRECATED warning. A trigger-like solution would be required. Did this concept came to your mind? 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? >> >> <<sanitize(&$a)>> >> 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 <mailto: >> 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

Dmitry Stogov

10 years ago
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.
> > 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.
> > 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)
> > 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.
> > 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 <<deprecated>>, 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 <<deprecated>> 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) Thanks. Dmitry.

Guilherme Blanco

10 years ago
On Fri, Apr 22, 2016 at 3:07 AM, Dmitry Stogov <dmitry@zend.com> 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 <<ORM\Entity>> 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; <<ORM\Entity("user")>> 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" } }
> > 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(<<Qux>> Bar $bar) : bool { // ... } } $reflClass = new \ReflectionClas("Foo"); $reflMethod = $reflClass->getMethod("bar"); $reflParameter = $reflMethod->getParameters()[0]; var_dump($reflParameter->getAttributes());
> > > > 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. 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; <<ORM\PrePersist>> public function prePersist() { $this->created = $this->updated = new \DateTime("now"); } <<ORM\PreUpdate>> public function preUpdate() { $this->updated = new \DateTime("now"); } } <<ORM\Entity>> 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.
> > 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 <<deprecated>>, 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 <<deprecated>> 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.
> 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? >>> >>> <<sanitize(&$a)>> >>> 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> >>> dmitry@zend.com <mailto: <dmitry@zend.com>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

Larry Garfield

10 years ago
On 4/22/16 10:39 AM, guilhermeblanco@gmail.com wrote:
> On Fri, Apr 22, 2016 at 3:07 AM, Dmitry Stogov <dmitry@zend.com> wrote: > > >> >> 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. > 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; > > <<ORM\PrePersist>> > public function prePersist() { > $this->created = $this->updated = new \DateTime("now"); > } > > <<ORM\PreUpdate>> > public function preUpdate() { > $this->updated = new \DateTime("now"); > } > } > > <<ORM\Entity>> > 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. >
Here's another example from a Doctrine-using project I built a while back. (Not the exact code, but the same concept; I've adapted it to PHP 7 types as well): interface Ownable { public function getOwner() : string; public function setOwner(string $u); public function isUnowned() : bool; } trait OwnableTrait { /** @ORM\String **/ private $owner = ''; public getOwner() : string { return $this->owner; } public setOwner(string $u) { $this->owner = $owner; } public function isUnowned() : bool { return $this->owner == ''; } } /** @ORM\Entity */ class Product implements Ownable { use OwnableTrait; // ... } class Widget extends Product { // ... } For annotations to work for this use case, reflecting on the properties of Widget would need to include $owner, and it would need to include the ORM\String annotation. (True regardless of whether annotations are array or object based.)
-- --Larry Garfield

Dmitry Stogov

10 years ago
On 04/22/2016 06:56 PM, Larry Garfield wrote:
> On 4/22/16 10:39 AM, guilhermeblanco@gmail.com wrote: >> On Fri, Apr 22, 2016 at 3:07 AM, Dmitry Stogov <dmitry@zend.com> wrote: >> >> >>> >>> 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. >> 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; >> >> <<ORM\PrePersist>> >> public function prePersist() { >> $this->created = $this->updated = new \DateTime("now"); >> } >> >> <<ORM\PreUpdate>> >> public function preUpdate() { >> $this->updated = new \DateTime("now"); >> } >> } >> >> <<ORM\Entity>> >> 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. >> > > Here's another example from a Doctrine-using project I built a while > back. (Not the exact code, but the same concept; I've adapted it to > PHP 7 types as well): > > interface Ownable { > public function getOwner() : string; > public function setOwner(string $u); > public function isUnowned() : bool; > } > > trait OwnableTrait { > > /** @ORM\String **/ > private $owner = ''; > > public getOwner() : string { > return $this->owner; > } > > public setOwner(string $u) { > $this->owner = $owner; > } > > public function isUnowned() : bool { > return $this->owner == ''; > } > } > > /** @ORM\Entity */ > class Product implements Ownable { > use OwnableTrait; > > // ... > } > > class Widget extends Product { > // ... > } > > For annotations to work for this use case, reflecting on the > properties of Widget would need to include $owner, and it would need > to include the ORM\String annotation. (True regardless of whether > annotations are array or object based.) >
In your example you don't override the property, so you'll able to see attributes of your property. <?php trait T { <<test>> protected $x; } class Foo { use T; } $r = new ReflectionClass("Foo"); $prop = $r->getProperty("x"); var_dump($prop->getAttributes()); ?> $ sapi/cli/php attr7.php array(1) { ["test"]=> bool(true) } Works fine. Thanks. Dmitry.

Pierrick Charron

10 years ago
On 22 April 2016 at 11:39, guilhermeblanco@gmail.com < guilhermeblanco@gmail.com> wrote:
> On Fri, Apr 22, 2016 at 3:07 AM, Dmitry Stogov <dmitry@zend.com> 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 > <<ORM\Entity>> 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; > > <<ORM\Entity("user")>> > 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" > } > } >
+1

Dmitry Stogov

10 years ago
On 04/22/2016 06:39 PM, guilhermeblanco@gmail.com wrote:
> > On Fri, Apr 22, 2016 at 3:07 AM, Dmitry Stogov <dmitry@zend.com > <mailto:dmitry@zend.com>> wrote: > > > > On 04/22/2016 04:05 AM, guilhermeblanco@gmail.com > <mailto: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 <<ORM\Entity>> 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; > > <<ORM\Entity("user")>> > 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.
> > >> >> 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(<<Qux>> 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.
> > > >> >> 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.
> 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; > > <<ORM\PrePersist>> > public function prePersist() { > $this->created = $this->updated = new \DateTime("now"); > } > > <<ORM\PreUpdate>> > public function preUpdate() { > $this->updated = new \DateTime("now"); > } > } > > <<ORM\Entity>> > 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).
> > >> >> 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 <<deprecated>>, 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 <<deprecated>> 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.

Guilherme Blanco

10 years ago
On Mon, Apr 25, 2016 at 3:42 AM, Dmitry Stogov <dmitry@zend.com> wrote:
> > > On 04/22/2016 06:39 PM, guilhermeblanco@gmail.com wrote: > > > On Fri, Apr 22, 2016 at 3:07 AM, Dmitry Stogov <dmitry@zend.com> wrote: > >> >> >> On 04/22/2016 04:05 AM, <guilhermeblanco@gmail.com> >> 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 <<ORM\Entity>> 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; > > <<ORM\Entity("user")>> > 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: <<ORM\Entity(tableName => "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(<<Qux>> 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(<<UserParameterConverter("userId")>> 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; > > <<ORM\PrePersist>> > public function prePersist() { > $this->created = $this->updated = new \DateTime("now"); > } > > <<ORM\PreUpdate>> > public function preUpdate() { > $this->updated = new \DateTime("now"); > } > } > > <<ORM\Entity>> > 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 <<deprecated>>, 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 <<deprecated>> 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> >> 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? >>>> >>>> <<sanitize(&$a)>> >>>> 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 <mailto: >>>> 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

Guilherme Blanco

10 years ago
Another thing that looks odd to me i that every time you call new ReflectionClass, a new reflection_object gets created. Isn't there a way to get this "cached" somehow in zend_class_entry? On Mon, Apr 25, 2016 at 10:11 AM, guilhermeblanco@gmail.com < guilhermeblanco@gmail.com> wrote:
> > > On Mon, Apr 25, 2016 at 3:42 AM, Dmitry Stogov <dmitry@zend.com> wrote: > >> >> >> On 04/22/2016 06:39 PM, guilhermeblanco@gmail.com wrote: >> >> >> On Fri, Apr 22, 2016 at 3:07 AM, Dmitry Stogov <dmitry@zend.com> wrote: >> >>> >>> >>> On 04/22/2016 04:05 AM, <guilhermeblanco@gmail.com> >>> 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 <<ORM\Entity>> 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; >> >> <<ORM\Entity("user")>> >> 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: <<ORM\Entity(tableName => "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(<<Qux>> 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(<<UserParameterConverter("userId")>> 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; >> >> <<ORM\PrePersist>> >> public function prePersist() { >> $this->created = $this->updated = new \DateTime("now"); >> } >> >> <<ORM\PreUpdate>> >> public function preUpdate() { >> $this->updated = new \DateTime("now"); >> } >> } >> >> <<ORM\Entity>> >> 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 <<deprecated>>, 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 <<deprecated>> 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> >>> 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? >>>>> >>>>> <<sanitize(&$a)>> >>>>> 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 <mailto: >>>>> 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 >
-- Guilherme Blanco Lead Architect at E-Block

Dmitry Stogov

10 years ago
This is another reason to use PHP layer on top on base attribute functionality. Implementing caching would take just 3 additional lines of PHP code. ________________________________ From: guilhermeblanco@gmail.com <guilhermeblanco@gmail.com> Sent: Monday, April 25, 2016 17:22 To: Dmitry Stogov Cc: Dominic Grostate; PHP internals Subject: Re: [PHP-DEV] [RFC] PHP Attributes Another thing that looks odd to me i that every time you call new ReflectionClass, a new reflection_object gets created. Isn't there a way to get this "cached" somehow in zend_class_entry? On Mon, Apr 25, 2016 at 10:11 AM, guilhermeblanco@gmail.com<mailto:guilhermeblanco@gmail.com> <guilhermeblanco@gmail.com<mailto:guilhermeblanco@gmail.com>> wrote: On Mon, Apr 25, 2016 at 3:42 AM, Dmitry Stogov <dmitry@zend.com<mailto:dmitry@zend.com>> wrote: On 04/22/2016 06:39 PM, guilhermeblanco@gmail.com<mailto:guilhermeblanco@gmail.com> wrote: On Fri, Apr 22, 2016 at 3:07 AM, Dmitry Stogov <dmitry@zend.com<mailto:dmitry@zend.com>> wrote: On 04/22/2016 04:05 AM, <mailto:guilhermeblanco@gmail.com> guilhermeblanco@gmail.com<mailto: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 <<ORM\Entity>> 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; <<ORM\Entity("user")>> 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: <<ORM\Entity(tableName => "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(<<Qux>> 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(<<UserParameterConverter("userId")>> 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; <<ORM\PrePersist>> public function prePersist() { $this->created = $this->updated = new \DateTime("now"); } <<ORM\PreUpdate>> public function preUpdate() { $this->updated = new \DateTime("now"); } } <<ORM\Entity>> 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 <<deprecated>>, 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 <<deprecated>> 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 <<mailto:dmitry@zend.com>dmitry@zend.com<mailto: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? <<sanitize(&$a)>> 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<mailto:dmitry@zend.com> <mailto:dmitry@zend.com<mailto: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 -- Guilherme Blanco Lead Architect at E-Block

Rowan Collins

10 years ago
guilhermeblanco@gmail.com wrote on 25/04/2016 15:22:
> Another thing that looks odd to me i that every time you call new > ReflectionClass, a new reflection_object gets created. > Isn't there a way to get this "cached" somehow in zend_class_entry?
I recently came upon a package that exists solely to work around this: https://packagist.org/packages/fsi/reflection I am reminded of the fact that people often say "reflection is slow, use it with care". Is this still the case? Does this limit the usability of attributes as proposed? Regards,
-- Rowan Collins [IMSoP]

Dmitry Stogov

10 years ago
On 04/25/2016 05:11 PM, guilhermeblanco@gmail.com wrote:
> > > On Mon, Apr 25, 2016 at 3:42 AM, Dmitry Stogov <dmitry@zend.com > <mailto:dmitry@zend.com>> wrote: > > > > On 04/22/2016 06:39 PM, guilhermeblanco@gmail.com > <mailto:guilhermeblanco@gmail.com> wrote: >> >> On Fri, Apr 22, 2016 at 3:07 AM, Dmitry Stogov <dmitry@zend.com >> <mailto:dmitry@zend.com>> wrote: >> >> >> >> On 04/22/2016 04:05 AM, guilhermeblanco@gmail.com >> <mailto: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 >> <<ORM\Entity>> 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; >> >> <<ORM\Entity("user")>> >> 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?
For example, I like to check an attribute existence, and I don't need to construct any objects.
> > 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: <<ORM\Entity(tableName => "user")>>
See a new example at https://wiki.php.net/rfc/attributes#use_cases It's already works. I don't see any reason to rewrite that few PHP lines in C.
> > > >> >> >>> >>> 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(<<Qux>> 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(<<UserParameterConverter("userId")>> > User $user = null) { > if ($user === null) { > throw new NotFoundException("User not found"); > } > > return ["me" => $this->getUser(), "user" => $user]; > } > }
I'll take a look into this, but you see, I'm not very interested in wasting time implementing particular low-level details while the whole idea is not accepted yet.
> >> >> >>> >>> 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... =(
If we had a theoretical approach for attribute inheritance, I would implement it. But I wouldn't invite any theory, because anyone is going to depend on use-case.
> > >> 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; >> >> <<ORM\PrePersist>> >> public function prePersist() { >> $this->created = $this->updated = new \DateTime("now"); >> } >> >> <<ORM\PreUpdate>> >> public function preUpdate() { >> $this->updated = new \DateTime("now"); >> } >> } >> >> <<ORM\Entity>> >> 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.
If we implement built-in @Inherit and/or @Override, it's not a big problem to copy attributes from parent. Thanks. Dmitry.

Larry Garfield

10 years ago
On 04/25/2016 10:20 AM, Dmitry Stogov wrote:
> >> This leads to some serious copy/paste, highly error prone... =( > > If we had a theoretical approach for attribute inheritance, I would > implement it. > But I wouldn't invite any theory, because anyone is going to depend on > use-case.
Realistically, I see only 3 possible approaches to consider: 1) An overridden class/property/method always inherits its parent's annotations/attributes, and you can add to them. 2) An overridden class/property/method never inherits its parent's annotations/attributes, but you can repeat them. 3) Give users a syntactic toggle.
>> 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. > > If we implement built-in @Inherit and/or @Override, it's not a big > problem to copy attributes from parent.
If I understand you correctly, that would be option 3. I would support that as well, as it's consistent with how methods work: By default, they override the parent method but you can call parent::whatever() to toggle the parent behavior back on. Thus, I would suggest that when overriding a class/property/method that has annotations, it NOT inherit annotations/attributes by default. However, by adding an annotation of <<__inherit>> (Or some other internal-reserved keyword, I don't have strong feelings on what it is) then the engine will automatically give you all the annotations off of the parent, and union them with whatever additional ones you add. That of course raises interesting questions around interfaces and traits, and how annotations on those comingle. Should annotations on interfaces auto-apply to their implementations or should they require an explicit <<__inherit>>? I'm undecided. --Larry Garfield

Sara Golemon

10 years ago
On Thu, Apr 21, 2016 at 2:13 PM, Dmitry Stogov <dmitry@zend.com> wrote:
> I would like to present an RFC proposing support for native annotation. >
I'm trying to imagine where the benefit of non-constant expressions comes in. <<foo($a < 1)>> Assuming we roll in php-ast at the same time (which is a big addition, IMO, and deserves its own separate RFC), what are users meant to do with this? Even if there's a use-case here, one could accomplish the same thing with: <<foo('$a < 1')>> And manually running that string into php-ast if that's what the caller wanted. Also, maybe I missed it, but I didn't see an answer to the question of ambiguity between parsing the above as a straight string, versus parsing it as a ZEND_AST_ZVAL. I'm sure the answer is "If the AST tree is just a ZVAL, then it's provided as the compile-time expression, but what about something else like this: <<foo(1+1)>> Logically, this is reducible to a single compile-time value, but it's a complex expression, so would it be the value int(2)? Or ZEND_AST_BINARY_OP(int(1), int(1))? I just think that over-engineers what should be a simple annotation feature. All that said, I love the proposal overall, and I can't wait to propose builtin annotations like <<__Memoize>>, <<__Mock>>, and similar. -Sara

Dmitry Stogov

10 years ago
On 04/22/2016 05:15 AM, Sara Golemon wrote:
> On Thu, Apr 21, 2016 at 2:13 PM, Dmitry Stogov <dmitry@zend.com> wrote: >> I would like to present an RFC proposing support for native annotation. >> > I'm trying to imagine where the benefit of non-constant expressions comes in. > > <<foo($a < 1)>> > > Assuming we roll in php-ast at the same time (which is a big addition, > IMO, and deserves its own separate RFC), what are users meant to do > with this? Even if there's a use-case here, one could accomplish the > same thing with: > > <<foo('$a < 1')>> > > And manually running that string into php-ast if that's what the caller wanted.
Good point. It's really not a big deal to run ast\parse_code($r->getAttributes()["foo"]) This would simplify the implementation a bit.
> > Also, maybe I missed it, but I didn't see an answer to the question of > ambiguity between parsing the above as a straight string, versus > parsing it as a ZEND_AST_ZVAL. I'm sure the answer is "If the AST > tree is just a ZVAL, then it's provided as the compile-time > expression, but what about something else like this: > > <<foo(1+1)>> > > Logically, this is reducible to a single compile-time value, but it's > a complex expression, so would it be the value int(2)? Or > ZEND_AST_BINARY_OP(int(1), int(1))?
Currently this going to be an AST - ZEND_AST_BINARY_OP(int(1), int(1))
> > I just think that over-engineers what should be a simple annotation feature.
This makes sense. I think, I'll follow your suggestion. The only missing ability is syntax check for <expression> attribute values.
> > All that said, I love the proposal overall, and I can't wait to > propose builtin annotations like <<__Memoize>>, <<__Mock>>, and > similar.
I'm looking forward as well. :) Thanks. Dmitry.

Jordi Boggiano

10 years ago
On 22/04/2016 08:17, Dmitry Stogov wrote:
> > > On 04/22/2016 05:15 AM, Sara Golemon wrote: >> On Thu, Apr 21, 2016 at 2:13 PM, Dmitry Stogov <dmitry@zend.com> wrote: >>> I would like to present an RFC proposing support for native annotation. >>> >> I'm trying to imagine where the benefit of non-constant expressions >> comes in. >> >> <<foo($a < 1)>> >> >> Assuming we roll in php-ast at the same time (which is a big addition, >> IMO, and deserves its own separate RFC), what are users meant to do >> with this? Even if there's a use-case here, one could accomplish the >> same thing with: >> >> <<foo('$a < 1')>> >> >> And manually running that string into php-ast if that's what the >> caller wanted. > > Good point. It's really not a big deal to run > ast\parse_code($r->getAttributes()["foo"]) > This would simplify the implementation a bit.
I can't speak for the implementation details, but I see a few benefits of having real AST vs a string.. You get syntax highlighting in editors, and most importantly compile time syntax errors if you messed up. Cheers
-- Jordi Boggiano @seldaek - http://seld.be

Fleshgrinder

10 years ago
On 4/22/2016 4:15 AM, Sara Golemon wrote:
> All that said, I love the proposal overall, and I can't wait to > propose builtin annotations like <<__Memoize>>, <<__Mock>>, and > similar. >
I'd rather see these two functionalities added as modifiers at the language level instead since they change the code behavior rather dramatically. Not sure if knowledge of them is of interest in stages where annotations might not be readily available; this was a driving factor in Kotlin's decision what becomes a modifier or annotation.[1] <?php final class A { private memoized function f() {} } test class B extends A { // OK, despite final. public function f() {} // Has access to private methods. } test class C extends B {} // OK class D extends B {} // Fatal Error! ?> I know that this is true for `@invariant`, `@require`, and `@ensure` as well, they are fully-fledged assertions after all. Especially the `@invariant` could be easily added as a magic method. [assert] assert.invariants = 1 <?php final class Object { public function __invariant() {} } ?> Both `@require` and `@ensure` are a bit harder to implement at language level. The following is the best I can come up with and is inspired by the Eiffel syntax.[2] [assert] assert.preconditions = 1 assert.postconditions = 1 <?php class PreconditionError extends AssertionError {} class PostconditionError extends AssertionError {} final class Account { public function withdraw(int $sum): void { $this->balance -= $this->balance - $sum; } require { $sum >= 0; $sum <= ($this->balance - self::MIN_BALANCE); } ensure { $this->balance === (old::$balance - $sum); } } $account = new Account; $account->withdraw(-10); /* Uncaught PreconditionError: $sum >= 0 */ $account->withdraw(10); /* Uncaught PreconditionError: $sum <= ($this->balance - self::MIN_BALANCE) */ $account->deposit(10); $account->withdraw(5); /* Uncaught PostconditionError: $this->balance === (old::$balance - $sum) */ ?> I actually think now that it would be better to go for this than for annotations. [1] http://blog.jetbrains.com/kotlin/2015/08/modifiers-vs-annotations/ [2] https://archive.eiffel.com/doc/online/eiffel50/intro/language/invitation-07.html
-- Richard "Fleshgrinder" Fussenegger

Fleshgrinder

10 years ago
The invariant could also be added as an additional branch to the class instead of a method, since it would not work like a method. class A {} invariant {} function f() {} require {} ensure {} This would also align nicely with closures and anonymous classes, which is kind a problematic with annotations. $A = new class {} invariant {}; $f = function () {} require {} ensure {}; The only thing that remains that might be of interest to both is `@throws` but that was not discussed at all as of yet. use Ns\SomeException; @throws SomeException function f() { } This is at least how one would expect it to work and it is something that should be covered by annotations and not as part of the language. The ABNF should account for that: ANNOTATION = "@" NAME [ " " VALUE ] NAME = STRING VALUE = QUOTED-STRING / PHP-REFERENCE / EXPRESSION QUOTED-STRING = ( "'" / DQUOTE ) STRING ( "'" / DQUOTE ) EXPRESSION = PHP-CODE ";" Where PHP references are any of the already possible ones: use F\Q\C\N; @annotation \F\Q\C\N @annotation N @annotation \F\Q\C\N::CONSTANT @annotation N::CONSTANT @annotation \F\Q\C\N::function() @annotation N::function() @annotation \F\Q\C\N::$variable @annotation N::$variable I also though some more about function support for annotations and this would actually be a nice thing for userland. annotation deprecated(Reflection $element, string $message = '') { @trigger_error($message, E_USER_DEPRECATED); } @deprecated('because') function x() {} @deprecated function y() {} This would allow users to implement simple and easy reactive annotations in userland. Even if this could or should be an extension of the feature in the future, it should be thought about know. Simply because the brackets make sense if such a feature is to be implemented. ;)
-- Richard "Fleshgrinder" Fussenegger

Benjamin Eberlei

10 years ago
On Sun, Apr 24, 2016 at 10:24 AM, Fleshgrinder <php@fleshgrinder.com> wrote:
> The invariant could also be added as an additional branch to the class > instead of a method, since it would not work like a method. > > class A {} invariant {} > > function f() {} require {} ensure {} > > This would also align nicely with closures and anonymous classes, which > is kind a problematic with annotations. >
You are way off topic with this imho.
> > $A = new class {} invariant {}; > > $f = function () {} require {} ensure {}; > > The only thing that remains that might be of interest to both is > `@throws` but that was not discussed at all as of yet. > > use Ns\SomeException; > > @throws SomeException > function f() { > > } > > This is at least how one would expect it to work and it is something > that should be covered by annotations and not as part of the language. > The ABNF should account for that: > > ANNOTATION = "@" NAME [ " " VALUE ] > NAME = STRING > VALUE = QUOTED-STRING / PHP-REFERENCE / EXPRESSION > QUOTED-STRING = ( "'" / DQUOTE ) STRING ( "'" / DQUOTE ) > EXPRESSION = PHP-CODE ";"
> Where PHP references are any of the already possible ones: > > use F\Q\C\N; > > @annotation \F\Q\C\N > @annotation N > @annotation \F\Q\C\N::CONSTANT > @annotation N::CONSTANT > @annotation \F\Q\C\N::function() > @annotation N::function() > @annotation \F\Q\C\N::$variable > @annotation N::$variable > > I also though some more about function support for annotations and this > would actually be a nice thing for userland. > > annotation deprecated(Reflection $element, string $message = '') { > @trigger_error($message, E_USER_DEPRECATED); > } > > @deprecated('because') > function x() {} > > @deprecated > function y() {} >
Attributes as proposed by Dimitry are not executing functions, they are only metadata that can be retrieved through reflection API. I think a python style decorator approach that you propose here, executing functions is not what the different pro-annotations, pro-attributes or pro-DesignByContract fractions want/need. You are proposing something along the lines of AOP, which is an entirely different beast in my opinion.

Fleshgrinder

10 years ago
On 4/24/2016 1:36 PM, Benjamin Eberlei wrote:
> On Sun, Apr 24, 2016 at 10:24 AM, Fleshgrinder <php@fleshgrinder.com> wrote: > >> The invariant could also be added as an additional branch to the class >> instead of a method, since it would not work like a method. >> >> class A {} invariant {} >> >> function f() {} require {} ensure {} >> >> This would also align nicely with closures and anonymous classes, which >> is kind a problematic with annotations. >> > > You are way off topic with this imho. >
No, not at all! The RFC explicitly mentions design by contract (DbC) and that is why I am responding to that. I do not think that annotations should or could be used for DbC and want to raise this issue here. Of course all of the above is a completely different story and requires its own RFC as well as implementation. However, DbC should not be used to justify the addition of annotations to PHP. (If libraries choose to use it for that, different story.) On 4/24/2016 1:36 PM, Benjamin Eberlei wrote:
> Attributes as proposed by Dimitry are not executing functions, they are > only metadata > that can be retrieved through reflection API. I think a python style > decorator approach > that you propose here, executing functions is not what the different > pro-annotations, > pro-attributes or pro-DesignByContract fractions want/need. > > You are proposing something along the lines of AOP, which is an entirely > different beast > in my opinion. >
I know and that is why I am writing that the usage of brackets is not a good idea because it suggests exactly that. We need to think about a possible feature as outlined by myself (reactive annotations or /Python style decorator approach/ as you call it) in order to not implement annotations in a way that would kill exactly such a feature in a possible future. Hence, I am not off topic because I am thinking outside of the box in the context of the whole ecosystem. Something that I see not happening in this RFC nor its discussion.
-- Richard "Fleshgrinder" Fussenegger

Benjamin Eberlei

10 years ago
On Sun, Apr 24, 2016 at 1:55 PM, Fleshgrinder <php@fleshgrinder.com> wrote:
> On 4/24/2016 1:36 PM, Benjamin Eberlei wrote: > > On Sun, Apr 24, 2016 at 10:24 AM, Fleshgrinder <php@fleshgrinder.com> > wrote: > > > >> The invariant could also be added as an additional branch to the class > >> instead of a method, since it would not work like a method. > >> > >> class A {} invariant {} > >> > >> function f() {} require {} ensure {} > >> > >> This would also align nicely with closures and anonymous classes, which > >> is kind a problematic with annotations. > >> > > > > You are way off topic with this imho. > > > > No, not at all! > > The RFC explicitly mentions design by contract (DbC) and that is why I > am responding to that.
It mentions DbC as one userland use-case for attributes. This proposal is a generic feature that allows DbC, but much other stuff like configuration (Annotation style). As a generic buidling block like Dimitry proposes them attributes are amazing and will allow many more different use-cases in userland, proposing anything less generic will not get wide-spread support considering that these kind of changes need 2/3 votes.
> I do not think that annotations should or could > be used for DbC and want to raise this issue here. Of course all of the > above is a completely different story and requires its own RFC as well > as implementation. However, DbC should not be used to justify the > addition of annotations to PHP. (If libraries choose to use it for that, > different story.) > > On 4/24/2016 1:36 PM, Benjamin Eberlei wrote: > > Attributes as proposed by Dimitry are not executing functions, they are > > only metadata > > that can be retrieved through reflection API. I think a python style > > decorator approach > > that you propose here, executing functions is not what the different > > pro-annotations, > > pro-attributes or pro-DesignByContract fractions want/need. > > > > You are proposing something along the lines of AOP, which is an entirely > > different beast > > in my opinion. > > > > I know and that is why I am writing that the usage of brackets is not a > good idea because it suggests exactly that. We need to think about a > possible feature as outlined by myself (reactive annotations or /Python > style decorator approach/ as you call it) in order to not implement > annotations in a way that would kill exactly such a feature in a > possible future.
> Hence, I am not off topic because I am thinking outside of the box in > the context of the whole ecosystem. Something that I see not happening > in this RFC nor its discussion. >
Using attributes as a generic building block, you could implement a PECL AOP extension doing the following: - in every call check if that function/method has attributes. - if it has an attribute "around(fn)", then call fn around the call - If it has an attribute "before(fn)", then call fn before the call - ... This is similar to what Dimitry wants to do, adding DbC as a pecl extension would be possible with attributes, by evaluating the AST Attribute nodes before each function call. This is why we need attributes in this generic way, because it opens up tons of possibilities in a way that a large majority of php contributors thinks this is worthwhile adding to the language. AOP in general however, i doubt will ever be in PHP core similar to DbC, but attributes allows to add them easily as PECL extensions.

Fleshgrinder

10 years ago
I am not arguing against the RFC nor the feature itself, on the contrary, I like it. I just do not like certain aspects and design decisions of it; that is all. Configuration and AOP are the best usecases for annotations and those should be stressed in the RFC. They are not mentioned at all! Another usecase that I am missing completely is the usage of it for documentation and static code analysis. I already mentioned the /throws/ annotation, this could help e.g. IDEs to warn you better about uncatched exceptions form methods you call. DbC is a possible usecase but better implemented at language level. The RFC could mention the possibility of it. However, right now it is the sole usecase beside the not very PHP applicable `<<inline>>` and `<<jit>>` examples. You see, this is more a problem of the RFC text and not of the feature. ;) Another think I complained about is the proposed syntax because it makes annotations look like function calls, which they simply are not and will not be. The syntax is misleading and a possible built-in functionality of reactive annotations (not saying we need them) at the language level for userland is blocked. I know I just repeated myself. The extension you mentioned works just fine without the brackets. @invariant CONDITION; class A { @ensure CONDITION; @require CONDITION; function f(){} }
-- Richard "Fleshgrinder" Fussenegger

Dmitry Stogov

10 years ago
On 04/24/2016 03:33 PM, Fleshgrinder wrote:
> I am not arguing against the RFC nor the feature itself, on the > contrary, I like it. I just do not like certain aspects and design > decisions of it; that is all. > > Configuration and AOP are the best usecases for annotations and those > should be stressed in the RFC. They are not mentioned at all! > > Another usecase that I am missing completely is the usage of it for > documentation and static code analysis. I already mentioned the /throws/ > annotation, this could help e.g. IDEs to warn you better about uncatched > exceptions form methods you call. > > DbC is a possible usecase but better implemented at language level. The > RFC could mention the possibility of it. However, right now it is the > sole usecase beside the not very PHP applicable `<<inline>>` and > `<<jit>>` examples. > > You see, this is more a problem of the RFC text and not of the feature. ;) > > Another think I complained about is the proposed syntax because it makes > annotations look like function calls, which they simply are not and will > not be. The syntax is misleading and a possible built-in functionality > of reactive annotations (not saying we need them) at the language level > for userland is blocked. I know I just repeated myself. > > The extension you mentioned works just fine without the brackets. > > @invariant CONDITION; > class A { > > @ensure CONDITION; > @require CONDITION; > function f(){} > > } >
The proposed by you "@..." syntax just won't fit into PHP grammar, because @ used as silence operator. Attribute, syntax is taken from HHVM. I don't see a big reason to introduce more fragmentation into PHP world. Personally I don't see "foo(a,b,c)" as a function, I see this as a predicate. It's possible to extend RFC with additional use-cases, but the longer the RFC the less people read it. Thanks. Dmitry.

Pierre Joye

10 years ago
hi Dmitry, Awesome work! Thanks. I do like you keep what was discussed last time. Make the basics available and leave the rest to the applications (doctrine or other). I feel too that how the apps will interact with may need some more discussions but other commented on it so I won't interfer here :). On Mon, Apr 25, 2016 at 3:17 PM, Dmitry Stogov <dmitry@zend.com> wrote:
> The proposed by you "@..." syntax just won't fit into PHP grammar, because @ > used as silence operator. > > Attribute, syntax is taken from HHVM. I don't see a big reason to introduce > more fragmentation into PHP world.
Also see https://docs.hhvm.com/hack/attributes/syntax and I fully agree with Dmitry here. It makes no sense now to go with our own syntax. If any of the hhvm developers are around, we may get some feedbacks about their current APIs (using getAttributes) and if they would like to change one thing or another. That could (or must) part of the PHP specifications. Cheers,
-- Pierre @pierrejoye | http://www.libgd.org

Stas Malyshev

10 years ago
Hi!
> The proposed by you "@..." syntax just won't fit into PHP grammar, > because @ used as silence operator.
Not sure why it is a problem more than << and >> being shift operators. Since you said it can't be applied to anonymous functions/classes anyway, parser with the help of AST should be able to figure out which one is meant - I don't see a case where it's ambiguous.
> Attribute, syntax is taken from HHVM. I don't see a big reason to > introduce more fragmentation into PHP world.
I don't think this is a good argument. It effectively means we now give our language design to Facebook Hack team - whatever they do in Hack, we then must follow, since otherwise it would be "more fragmentation". I don't think it is a good position on our side.
-- Stas Malyshev smalyshev@gmail.com

Dmitry Stogov

10 years ago
On 04/24/2016 11:24 AM, Fleshgrinder wrote:
> The invariant could also be added as an additional branch to the class > instead of a method, since it would not work like a method. > > class A {} invariant {} > > function f() {} require {} ensure {} > > This would also align nicely with closures and anonymous classes, which > is kind a problematic with annotations. > > $A = new class {} invariant {}; > > $f = function () {} require {} ensure {}; > > The only thing that remains that might be of interest to both is > `@throws` but that was not discussed at all as of yet. > > use Ns\SomeException; > > @throws SomeException > function f() { > > } > > This is at least how one would expect it to work and it is something > that should be covered by annotations and not as part of the language. > The ABNF should account for that: > > ANNOTATION = "@" NAME [ " " VALUE ] > NAME = STRING > VALUE = QUOTED-STRING / PHP-REFERENCE / EXPRESSION > QUOTED-STRING = ( "'" / DQUOTE ) STRING ( "'" / DQUOTE ) > EXPRESSION = PHP-CODE ";" > > Where PHP references are any of the already possible ones: > > use F\Q\C\N; > > @annotation \F\Q\C\N > @annotation N > @annotation \F\Q\C\N::CONSTANT > @annotation N::CONSTANT > @annotation \F\Q\C\N::function() > @annotation N::function() > @annotation \F\Q\C\N::$variable > @annotation N::$variable > > I also though some more about function support for annotations and this > would actually be a nice thing for userland. > > annotation deprecated(Reflection $element, string $message = '') { > @trigger_error($message, E_USER_DEPRECATED); > } > > @deprecated('because') > function x() {} > > @deprecated > function y() {} > > This would allow users to implement simple and easy reactive annotations > in userland. Even if this could or should be an extension of the feature > in the future, it should be thought about know. Simply because the > brackets make sense if such a feature is to be implemented. ;) >
Looks interesting, but try to think where the "annotation deprecated" should be declared,to be visible in every compiled script, when the corresponding code should be called (in what context), what if we need to do something at compile-time? Single answers to these question are going to be great for "deprecated" use case, however they will limit usability for other cases. Thanks. Dmitry.

Fleshgrinder

10 years ago
On 4/25/2016 10:04 AM, Dmitry Stogov wrote:
> > > On 04/24/2016 11:24 AM, Fleshgrinder wrote: >> The invariant could also be added as an additional branch to the class >> instead of a method, since it would not work like a method. >> >> class A {} invariant {} >> >> function f() {} require {} ensure {} >> >> This would also align nicely with closures and anonymous classes, which >> is kind a problematic with annotations. >> >> $A = new class {} invariant {}; >> >> $f = function () {} require {} ensure {}; >> >> The only thing that remains that might be of interest to both is >> `@throws` but that was not discussed at all as of yet. >> >> use Ns\SomeException; >> >> @throws SomeException >> function f() { >> >> } >> >> This is at least how one would expect it to work and it is something >> that should be covered by annotations and not as part of the language. >> The ABNF should account for that: >> >> ANNOTATION = "@" NAME [ " " VALUE ] >> NAME = STRING >> VALUE = QUOTED-STRING / PHP-REFERENCE / EXPRESSION >> QUOTED-STRING = ( "'" / DQUOTE ) STRING ( "'" / DQUOTE ) >> EXPRESSION = PHP-CODE ";" >> >> Where PHP references are any of the already possible ones: >> >> use F\Q\C\N; >> >> @annotation \F\Q\C\N >> @annotation N >> @annotation \F\Q\C\N::CONSTANT >> @annotation N::CONSTANT >> @annotation \F\Q\C\N::function() >> @annotation N::function() >> @annotation \F\Q\C\N::$variable >> @annotation N::$variable >> >> I also though some more about function support for annotations and this >> would actually be a nice thing for userland. >> >> annotation deprecated(Reflection $element, string $message = '') { >> @trigger_error($message, E_USER_DEPRECATED); >> } >> >> @deprecated('because') >> function x() {} >> >> @deprecated >> function y() {} >> >> This would allow users to implement simple and easy reactive annotations >> in userland. Even if this could or should be an extension of the feature >> in the future, it should be thought about know. Simply because the >> brackets make sense if such a feature is to be implemented. ;) >> > Looks interesting, but try to think where the "annotation deprecated" > should be declared,to be visible in every compiled script, when the > corresponding code should be called (in what context), what if we need > to do something at compile-time? Single answers to these question are > going to be great for "deprecated" use case, however they will limit > usability for other cases. > > Thanks. Dmitry. >
Reactive annotations would have their limitations, e.g. calls to the reactive annotation function only happen during runtime and any attempts to perform something at compile time directly results in an engine error. It limits their use cases but allows interesting ones at the same time. Another approach could be to allow registration of reactive annotation functions at runtime, this would make it even more useful. Of course that would mean that the function is only called for each encountered annotation at runtime after it was registered. However, such a limitation is pretty much the same as we have it for any other function right now too. class User { public function hasRole($right) { if (false) { throw new InsufficientPrivileges; } } } $user = new User(); register_annotation('Framework\Role', [ $user, 'hasRole' ]); class Controller { @Framework\Role('admin') public function editAction(){} } // ENGINE (pseudo code) array registered_annotations = []; void register_reactive_annotation(annotation, callback) { registered_annotations[annotation] = callback; } void reactive_annotation_call(annotation, ...arguments) { if (annotation exists in registered_annotations) { call registered_annotations[annotation](...arguments); } else { log_info("Unknown reactive annotation %s", annotation); } } However, this is a story for another RFC. It is only important because usage of the brackets in the current RFC would make such a feature more complicated to implement.
-- Richard "Fleshgrinder" Fussenegger

Lester Caine

10 years ago
On 21/04/16 22:13, Dmitry Stogov wrote:
> I would like to present an RFC proposing support for native annotation.
I thought that the debate had been completed on annotation, and since most of the work can be done in a stand alone extension, the various parties were going to take that route and develop working options which could then be compared to decide if any provide a suitable replacement for the current docbloc 'process' that most of the IDE's currently recognise. There is room for proper documentation of the current standard as some elements are not followed reliably, but on the whole the system works, and any of the new extensions need to be addressed via IDE projects before they can replace that.
-- Lester Caine - G8HFL ----------------------------- Contact - http://lsces.co.uk/wiki/?page=contact L.S.Caine Electronic Services - http://lsces.co.uk EnquirySolve - http://enquirysolve.com/ Model Engineers Digital Workshop - http://medw.co.uk Rainbow Digital Media - http://rainbowdigitalmedia.co.uk

Thomas Punt

10 years ago
Hi Dmitry! > 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. Just a couple of comments on this: 1. I'd definitely reuse the php-ast extension for parsing the code into an AST. It performs a number of transformations on PHP's underlying AST that make it much nicer to use (namely better consistency). It is also less fragile by having the abstraction between PHP's internal AST and the AST that is exposed to userland (enabling for internal AST changes without impacting the AST exposed to userland). 2. You mentioned about moving some of the php-ast extension into core. I wonder if it would be better to just move the whole extension into the core first, and then enable this functionality if the php-ast extension is enabled. Also, slightly tangential, but the RFC says attributes are supported on class constants, yet doc comments (IIRC) are not. I wonder if support for doc comments should be added for class constants? Thanks, Tom

Dmitry Stogov

10 years ago
On 04/22/2016 02:46 PM, Thomas Punt wrote:
> Hi Dmitry! > >> 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. > Just a couple of comments on this: > > 1. I'd definitely reuse the php-ast extension for parsing the code into an > AST. It performs a number of transformations on PHP's underlying AST > that make it much nicer to use (namely better consistency). It is also > less fragile by having the abstraction between PHP's internal AST and > the AST that is exposed to userland (enabling for internal AST changes > without impacting the AST exposed to userland).
I'm not sure. Both approaches make sense.
> > 2. You mentioned about moving some of the php-ast extension into core. > I wonder if it would be better to just move the whole extension into the > core first, and then enable this functionality if the php-ast extension is > enabled.
Even if we move php-ast into core (I think we will do it), it's going to be optional. However attributes should always work.
> > Also, slightly tangential, but the RFC says attributes are supported on > class constants, yet doc comments (IIRC) are not. I wonder if support > for doc comments should be added for class constants?
it is already implemented. Thanks. Dmitry.

Thomas Punt

10 years ago
Hi! > From: dmitry@zend.com >> On 04/22/2016 02:46 PM, Thomas Punt wrote: >> Hi Dmitry! >> >> Just a couple of comments on this: >> >> 1. I'd definitely reuse the php-ast extension for parsing the code into an >> AST. It performs a number of transformations on PHP's underlying AST >> that make it much nicer to use (namely better consistency). It is also >> less fragile by having the abstraction between PHP's internal AST and >> the AST that is exposed to userland (enabling for internal AST changes >> without impacting the AST exposed to userland). > > I'm not sure. Both approaches make sense. Whilst fragility is a concern, a bigger problem with not reusing ast\parse_code() is that the AST produced will be different in even the simplest of cases. For example, take the following simple expression: $a> 2 With your attributes branch, this will produce an AST of: object(ast\Node)#2 (4) {     ["kind"]=> int(521) // ZEND_AST_GREATER     ["flags"]=> int(0)     ["lineno"]=> int(1)     ["children"]=> [...] } Whereas with ast\parse_code(), the following AST will be produced: object(ast\Node)#2 (4) {     ["kind"]=> int(520) // AST_BINARY_OP     ["flags"]=> int(256) // BINARY_IS_GREATER     ["lineno"]=> int(1)     ["children"]=> [...] } The php-ast extension transforms the special node types for>,>=, <, <= into AST_BINARY_OP nodes with different flags set. This is just one difference of many between the internal AST and the AST produced by the php-ast extension. >> 2. You mentioned about moving some of the php-ast extension into core. >> I wonder if it would be better to just move the whole extension into the >> core first, and then enable this functionality if the php-ast extension is >> enabled. > > Even if we move php-ast into core (I think we will do it), it's going to > be optional. > However attributes should always work. If we're going to reuse ast\parse_code(), then we will need the whole php-ast extension in the core. It could therefore be made similar to the pcre, date, Reflection, SPL, etc extensions, where it cannot be disabled. >> Also, slightly tangential, but the RFC says attributes are supported on >> class constants, yet doc comments (IIRC) are not. I wonder if support >> for doc comments should be added for class constants? > > it is already implemented. Oh right, my mistake. It does not seem to be exposed through php-ast or reflection then. > Thanks. Dmitry. >> >> Thanks, >> Tom -Tom

Dmitry Stogov

10 years ago
On 04/24/2016 05:02 PM, Thomas Punt wrote:
> Hi! > >> From: dmitry@zend.com >>> On 04/22/2016 02:46 PM, Thomas Punt wrote: >>> Hi Dmitry! >>> >>> Just a couple of comments on this: >>> >>> 1. I'd definitely reuse the php-ast extension for parsing the code into an >>> AST. It performs a number of transformations on PHP's underlying AST >>> that make it much nicer to use (namely better consistency). It is also >>> less fragile by having the abstraction between PHP's internal AST and >>> the AST that is exposed to userland (enabling for internal AST changes >>> without impacting the AST exposed to userland). >> I'm not sure. Both approaches make sense. > Whilst fragility is a concern, a bigger problem with not reusing > ast\parse_code() is that the AST produced will be different in even the > simplest of cases. For example, take the following simple expression: > > $a> 2 > > With your attributes branch, this will produce an AST of: > > object(ast\Node)#2 (4) { > ["kind"]=> int(521) // ZEND_AST_GREATER > ["flags"]=> int(0) > ["lineno"]=> int(1) > ["children"]=> [...] > } > > Whereas with ast\parse_code(), the following AST will be produced: > > object(ast\Node)#2 (4) { > ["kind"]=> int(520) // AST_BINARY_OP > ["flags"]=> int(256) // BINARY_IS_GREATER > ["lineno"]=> int(1) > ["children"]=> [...] > } > > The php-ast extension transforms the special node types for>,>=, <, <= > into AST_BINARY_OP nodes with different flags set. This is just one > difference of many between the internal AST and the AST produced by > the php-ast extension.
This is initial implementation details, and they are going to be fixed. If we decide to use AST in attributes, getAttributes() outout are going to be 100% compatible with php-ast.
> >>> 2. You mentioned about moving some of the php-ast extension into core. >>> I wonder if it would be better to just move the whole extension into the >>> core first, and then enable this functionality if the php-ast extension is >>> enabled. >> Even if we move php-ast into core (I think we will do it), it's going to >> be optional. >> However attributes should always work. > If we're going to reuse ast\parse_code(), then we will need the whole php-ast > extension in the core. It could therefore be made similar to the pcre, date, > Reflection, SPL, etc extensions, where it cannot be disabled.
Right, but this is out of scope of this RFC. Nikita was going to propose moving php-ast into core soon. Thanks. Dmitry.

Fleshgrinder

10 years ago
+1 for the basic idea, however, I have various remarks. The RFC text is hard to read and contains many grammatical mistakes. How could one help you here? I think that the Hack name attributes is unintelligible and annotations would be much clearer to any audience. Simply because the name is very well known. I do not see the need for multi-annotation nor multi-value support. It just creates multiple ways to achieve the exact same thing for no good reason. I do not like the <<>> syntax. It requires many key strokes, is hard to read, and looks ugly. Why not simply @ and be done with it. I am not so sure about the bracket requirement, is it somehow required for the parsing? Otherwise I would leave it off. I guess it might be hard to find the end of an annotation but have you considered to use the semicolon for that? Would align nicely with existing PHP syntax. The following would be the ABNF for my proposal: ANNOTATION = "@" NAME [ " " VALUE ] NAME = STRING VALUE = QUOTED-STRING / PHP-CONSTANT / EXPRESSION QUOTED-STRING = ( "'" / DQUOTE ) STRING ( "'" / DQUOTE ) EXPRESSION = PHP-CODE ";" A semicolon would only be required if it is not a single quoted string (see following example) or constant. A question that I see unanswered is how embedding of variables in quoted strings should be dealt with. I see no need for this but people might assume it is supported because PHP supports it everywhere else. <?php $name = "Richard Fussenegger"; $email = "php@fleshgrinder.com"; @author "{$name} <{$email}>" class A {} <<author("{$name} <{$email}>")>> class B {} ?> Requiring PHP code to be terminated with a semicolon should also ensure that people do not start to write fully-fledged programs in annotations. Since that is not what they are intended for. An alternative approach I see here would be to go for the brackets but then again only for PHP code: EXPRESSION = "(" PHP-CODE ")" Then again, it looks similar to a function call and this is imho not good. Unless of course new annotations can be defined as special functions directly in userland, e.g. with an `annotation function` and/or `annotation class`. However, this would require more thought. Another question that is unanswered for me is: how to go about adding annotations to a complete file as is currently possible with PhpDoc and its file-level doc block: <?php @author 'Richard Fussenegger <php@fleshgrinder.com>' @copyright '2016 Richard Fussenegger' @license 'MIT' declare(strict_types=1); namespace Fleshgrinder\PhpInternals; @description 'True annotation support could be a very good thing.' @invariant $this->balance >= self::MIN_BALANCE; class Account { private const int MIN_BALANCE = 0; private int $balance; private Person $owner; @require $sum >= 0; @ensure $this->balance === (old::$balance + $sum); public function deposit(int $sum): void { $this->balance += $sum; } @require $sum >= 0; @require $sum <= $this->balance - self::MIN_BALANCE; @ensure $this->balance === (old::$balance - $sum); public function withdraw(int $sum): void { $this->balance -= $sum; } @deprecated 'for various reasons' public function setOwner(Person $wner): void { $this->owner = $owner; } } @inheritDoc class OverdraftAccount extends Account { private const int MIN_BALANCE = -1000; } ?> We also need to make sure to add something regarding coding standards for annotation names. I would propose to go for camelCase (same as for method names) since this is what PhpDoc used to use for many years now. We also need to define how people can avoid to collide with internal annotations. The typical double-underscore prefix approach that we have for magic methods creates a lot of visual clutter and looks weird if spread among all kinds of places. A namespace approach as we have it already for userland code could help here. <?php use Doctrine\ORM; @ORM::entity @ORM::table [ 'name' => 'user', 'unique_constraints' => [ 'name' => 'user_unique', 'columns' => [ 'username' ], ], 'indexes' => [ 'name' => 'user_idx', 'clumns' => [ 'email' ], ], 'schema' => 'schema_name', ]; class User {} ?>
-- Richard "Fleshgrinder" Fussenegger

Dmitry Stogov

10 years ago
On 04/23/2016 06:29 PM, Fleshgrinder wrote:
> +1 for the basic idea, however, I have various remarks. > > The RFC text is hard to read and contains many grammatical mistakes. How > could one help you here?
I would need a co-author :)
> > I think that the Hack name attributes is unintelligible and annotations > would be much clearer to any audience. Simply because the name is very > well known.
Different languages names this differently. I may add an additional voting question - "annotation vs attributes?".
> > I do not see the need for multi-annotation nor multi-value support. It > just creates multiple ways to achieve the exact same thing for no good > reason.
completely disagree. Each value in multi-value attribute may have its own meaning. e.g. <<if(Condition,OnTrue,OnFalse)>>
> > I do not like the <<>> syntax. It requires many key strokes, is hard to > read, and looks ugly. Why not simply @ and be done with it. I am not so > sure about the bracket requirement, is it somehow required for the > parsing? Otherwise I would leave it off. I guess it might be hard to > find the end of an annotation but have you considered to use the > semicolon for that? Would align nicely with existing PHP syntax. The > following would be the ABNF for my proposal: > > ANNOTATION = "@" NAME [ " " VALUE ] > NAME = STRING > VALUE = QUOTED-STRING / PHP-CONSTANT / EXPRESSION > QUOTED-STRING = ( "'" / DQUOTE ) STRING ( "'" / DQUOTE ) > EXPRESSION = PHP-CODE ";" > > A semicolon would only be required if it is not a single quoted string > (see following example) or constant. A question that I see unanswered is > how embedding of variables in quoted strings should be dealt with. I see > no need for this but people might assume it is supported because PHP > supports it everywhere else. > > <?php > > $name = "Richard Fussenegger"; > $email = "php@fleshgrinder.com"; > > @author "{$name} <{$email}>" > class A {} > > <<author("{$name} <{$email}>")>> > class B {} > > ?> > > Requiring PHP code to be terminated with a semicolon should also ensure > that people do not start to write fully-fledged programs in annotations. > Since that is not what they are intended for. An alternative approach I > see here would be to go for the brackets but then again only for PHP code: > > EXPRESSION = "(" PHP-CODE ")" > > Then again, it looks similar to a function call and this is imho not > good. Unless of course new annotations can be defined as special > functions directly in userland, e.g. with an `annotation function` > and/or `annotation class`. However, this would require more thought. > > Another question that is unanswered for me is: how to go about adding > annotations to a complete file as is currently possible with PhpDoc and > its file-level doc block: > > <?php > @author 'Richard Fussenegger <php@fleshgrinder.com>' > @copyright '2016 Richard Fussenegger' > @license 'MIT' > > declare(strict_types=1); > > namespace Fleshgrinder\PhpInternals; > > @description 'True annotation support could be a very good thing.' > @invariant $this->balance >= self::MIN_BALANCE; > class Account { > > private const int MIN_BALANCE = 0; > > private int $balance; > > private Person $owner; > > @require $sum >= 0; > @ensure $this->balance === (old::$balance + $sum); > public function deposit(int $sum): void { > $this->balance += $sum; > } > > @require $sum >= 0; > @require $sum <= $this->balance - self::MIN_BALANCE; > @ensure $this->balance === (old::$balance - $sum); > public function withdraw(int $sum): void { > $this->balance -= $sum; > } > > @deprecated 'for various reasons' > public function setOwner(Person $wner): void { > $this->owner = $owner; > } > > } > > @inheritDoc > class OverdraftAccount extends Account { > > private const int MIN_BALANCE = -1000; > > } > > ?>
You should try to implement this syntax to understand the problem. It leads to parse conflicts.
> We also need to make sure to add something regarding coding standards > for annotation names. I would propose to go for camelCase (same as for > method names) since this is what PhpDoc used to use for many years now.
This RFC is not going to propose coding standards.
> > We also need to define how people can avoid to collide with internal > annotations. The typical double-underscore prefix approach that we have > for magic methods creates a lot of visual clutter and looks weird if > spread among all kinds of places. A namespace approach as we have it > already for userland code could help here. > > <?php > > use Doctrine\ORM; > > @ORM::entity > @ORM::table [ > 'name' => 'user', > 'unique_constraints' => [ > 'name' => 'user_unique', > 'columns' => [ 'username' ], > ], > 'indexes' => [ > 'name' => 'user_idx', > 'clumns' => [ 'email' ], > ], > 'schema' => 'schema_name', > ]; > class User {} > > ?> >
Agree. Namespaces looks better than "__" prefixes. Thanks. Dmitry.

Niklas Keller

10 years ago
> > I think that the Hack name attributes is unintelligible and annotations >> would be much clearer to any audience. Simply because the name is very >> well known. >> > > Different languages names this differently. > I may add an additional voting question - "annotation vs attributes?".
The description on https://wiki.php.net/rfc even uses "annotation" to explain what "PHP Attributes" is, so I think "annotation"is the right naming choice. PHP Attributes
> Native support for annotation (Discussion began 2016-04-22)
Annotation is well known and unambiguous. Regards, Niklas.

Fleshgrinder

10 years ago
On 4/25/2016 10:31 AM, Dmitry Stogov wrote:
> > > On 04/23/2016 06:29 PM, Fleshgrinder wrote: >> +1 for the basic idea, however, I have various remarks. >> >> The RFC text is hard to read and contains many grammatical mistakes. How >> could one help you here? > I would need a co-author :) >
I would love to help you but would require some karma. I need to check how to get it. I will answer more tomorrow.
-- Richard "Fleshgrinder" Fussenegger

Fleshgrinder

10 years ago
On 4/25/2016 10:31 AM, Dmitry Stogov wrote:
> completely disagree. > Each value in multi-value attribute may have its own meaning. e.g. > <<if(Condition,OnTrue,OnFalse)>> >
That is a different example but I think that I misinterpreted it anyways. <<test(1,2)>> function foo() {} I thought that the above would result in the following: reflect foo => [ test => 1 test => 2 ] But I think in reality it results int: reflect foo => [ test => [ 1, 2 ] ] In case the latter is true, than everything is fine. :) On 4/25/2016 10:31 AM, Dmitry Stogov wrote:
> You should try to implement this syntax to understand the problem. > It leads to parse conflicts. >
The inclusion of the semicolon definitely does, yes. Usage of the @ should not but Stanislav already offered to jump in. I am not fit enough yet with the php-src to take up such a big challenge. On 4/25/2016 10:31 AM, Dmitry Stogov wrote:
> This RFC is not going to propose coding standards. >
Only talking about a standard for internal attributes, userland can and should do whatever they want. But we need to ensure consistency for internal attributes and that userland is not going to collide with internal ones. Currently the RFC does not include any attributes but many examples that others might use to deduce a coding standard and later we add internal attributes and things go south.
-- Richard "Fleshgrinder" Fussenegger

Yasuo Ohgaki

10 years ago
Hi Dimitry, On Fri, Apr 22, 2016 at 6:13 AM, Dmitry Stogov <dmitry@zend.com> wrote:
> 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.
Nice RFC! Attributes are always evaluated, right? i.e. No INI switch nor declare to control behavior. Just making sure. Regards,
-- Yasuo Ohgaki yohgaki@ohgaki.net

Dmitry Stogov

10 years ago
On 04/24/2016 12:34 AM, Yasuo Ohgaki wrote:
> Hi Dimitry, > > On Fri, Apr 22, 2016 at 6:13 AM, Dmitry Stogov <dmitry@zend.com> wrote: >> 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. > Nice RFC! > Attributes are always evaluated, right? i.e. No INI switch nor declare > to control behavior. > Just making sure.
Attributes are always parsed together with PHP script and stored internally. Then you may access them through Reflection*::getAttributes() or in C extensions through native API. AST in attribute values is not evaluated. Thanks. Dmitry.

Sebastian Bergmann

10 years ago
On 04/21/2016 11:13 PM, Dmitry Stogov wrote:
> I would like to present an RFC proposing support for native annotation.
Dmitry, please use "annotation" as the name for this feature and not "attribute". We already have attributes: it's what we use to store data in objects. And while some people call them "member variables" or "properties" the correct term for them (AFAIK) is "attribute". Best, Sebastian

Zeev Suraski

10 years ago
> -----Original Message----- > From: Sebastian Bergmann [mailto:sebastian@php.net] > Sent: Sunday, April 24, 2016 12:14 PM > To: internals@lists.php.net > Subject: Re: [PHP-DEV] [RFC] PHP Attributes > > On 04/21/2016 11:13 PM, Dmitry Stogov wrote: > > I would like to present an RFC proposing support for native annotation. > > Dmitry, > > please use "annotation" as the name for this feature and not "attribute". > > We already have attributes: it's what we use to store data in objects. And > while some people call them "member variables" or "properties" the correct > term for them (AFAIK) is "attribute".
I don't think I've ever heard properties referred to as attributes in the context of PHP. The PHP manual defines them and refers to them throughout as 'properties'. (I'm not voicing an opinion on attributes vs. annotations; Just pointing out that data storage inside objects isn't named attributes). Zeev

Fleshgrinder

10 years ago
On 4/24/2016 1:00 PM, Zeev Suraski wrote:
> > >> -----Original Message----- >> From: Sebastian Bergmann [mailto:sebastian@php.net] >> Sent: Sunday, April 24, 2016 12:14 PM >> To: internals@lists.php.net >> Subject: Re: [PHP-DEV] [RFC] PHP Attributes >> >> On 04/21/2016 11:13 PM, Dmitry Stogov wrote: >>> I would like to present an RFC proposing support for native annotation. >> >> Dmitry, >> >> please use "annotation" as the name for this feature and not "attribute". >> >> We already have attributes: it's what we use to store data in objects. And >> while some people call them "member variables" or "properties" the correct >> term for them (AFAIK) is "attribute". > > I don't think I've ever heard properties referred to as attributes in the context of PHP. The PHP manual defines them and refers to them throughout as 'properties'. > > (I'm not voicing an opinion on attributes vs. annotations; Just pointing out that data storage inside objects isn't named attributes). > > Zeev >
The terminology here is pretty clear and you can just look it up, it has nothing to do with PHP or Java or whatever. The main problem is simply that people do not know the terminology and get mixed up. That is usually okay in a casual discussion where each peer understands what the other is referring to. However, we are dealing with language specification here and need to be very exact or we fuck it up for the users. `$x` is a property in the following example: class A { $x; } https://en.wikipedia.org/wiki/Property_%28programming%29 `private` is an attribute of property `$x` and an access modifier (not visibility, because the property is still visible, e.g. via reflection, and it only alters access to the property): class A { private $x; } https://en.wikipedia.org/wiki/Attribute_%28computing%29 Note that these things go further up in the terminology hierarchy to: Field - https://en.wikipedia.org/wiki/Field_%28computer_science%29 Record - https://en.wikipedia.org/wiki/Record_%28computer_science%29 Data Structure - https://en.wikipedia.org/wiki/Data_structure This is not the whole image yet, there are more things like (random order): - functions - methods - members - class variables - static variables - behavior - data - ... You will notice, if you read all Wikipedia articles and related documents, that these terms are too often used interchangeable. This is, as mentioned before, mainly the case because people to not care about terminology in general. To sum it up, calling them attributes is outright wrong because not all parts of a source code of a programming language has attributes. That is why Sun chose the word annotation. Annotation means metadata that was added to data. It does not say anything about the data itself and it does not necessarily alter the data in any way, however, it can. This is exactly what this feature is about. Since, an entity annotation does not alter the data (in this case a class) itself but this additional metadata may be used to configure other parts of the program. The same is true for a method that has been annotated with test. The data is not altered at all but is helps other parts of the program to configure specific data and behavior based on this additional metadata that was added to the data. Execution of the same method without those other parts that react on the metadata does not do anything and the program will execute normally. TL;DR this feature *MUST* be called annotations (unless someone knows an even better term) and the annotations *MAY* be used to add attributes to properties but for many other things too.
-- Richard "Fleshgrinder" Fussenegger

Benjamin Eberlei

10 years ago
On Sun, Apr 24, 2016 at 1:48 PM, Fleshgrinder <php@fleshgrinder.com> wrote:
> On 4/24/2016 1:00 PM, Zeev Suraski wrote: > > > > > >> -----Original Message----- > >> From: Sebastian Bergmann [mailto:sebastian@php.net] > >> Sent: Sunday, April 24, 2016 12:14 PM > >> To: internals@lists.php.net > >> Subject: Re: [PHP-DEV] [RFC] PHP Attributes > >> > >> On 04/21/2016 11:13 PM, Dmitry Stogov wrote: > >>> I would like to present an RFC proposing support for native annotation. > >> > >> Dmitry, > >> > >> please use "annotation" as the name for this feature and not > "attribute". > >> > >> We already have attributes: it's what we use to store data in objects. > And > >> while some people call them "member variables" or "properties" the > correct > >> term for them (AFAIK) is "attribute". > > > > I don't think I've ever heard properties referred to as attributes in > the context of PHP. The PHP manual defines them and refers to them > throughout as 'properties'. > > > > (I'm not voicing an opinion on attributes vs. annotations; Just > pointing out that data storage inside objects isn't named attributes). > > > > Zeev > > > > The terminology here is pretty clear and you can just look it up, it has > nothing to do with PHP or Java or whatever. The main problem is simply > that people do not know the terminology and get mixed up. That is > usually okay in a casual discussion where each peer understands what the > other is referring to. However, we are dealing with language > specification here and need to be very exact or we fuck it up for the > users. > > `$x` is a property in the following example: > > class A { > $x; > } > > https://en.wikipedia.org/wiki/Property_%28programming%29 > > `private` is an attribute of property `$x` and an access modifier (not > visibility, because the property is still visible, e.g. via reflection, > and it only alters access to the property): > > class A { > private $x; > } > > https://en.wikipedia.org/wiki/Attribute_%28computing%29 > > Note that these things go further up in the terminology hierarchy to: > Field - https://en.wikipedia.org/wiki/Field_%28computer_science%29 > Record - https://en.wikipedia.org/wiki/Record_%28computer_science%29 > Data Structure - https://en.wikipedia.org/wiki/Data_structure > > This is not the whole image yet, there are more things like (random order): > - functions > - methods > - members > - class variables > - static variables > - behavior > - data > - ... > > You will notice, if you read all Wikipedia articles and related > documents, that these terms are too often used interchangeable. This is, > as mentioned before, mainly the case because people to not care about > terminology in general. > > To sum it up, calling them attributes is outright wrong because not all > parts of a source code of a programming language has attributes. That is > why Sun chose the word annotation. Annotation means metadata that was > added to data. It does not say anything about the data itself and it > does not necessarily alter the data in any way, however, it can. This is > exactly what this feature is about. Since, an entity annotation does not > alter the data (in this case a class) itself but this additional > metadata may be used to configure other parts of the program. > > The same is true for a method that has been annotated with test. The > data is not altered at all but is helps other parts of the program to > configure specific data and behavior based on this additional metadata > that was added to the data. Execution of the same method without those > other parts that react on the metadata does not do anything and the > program will execute normally. > > TL;DR this feature *MUST* be called annotations (unless someone knows an > even better term) and the annotations *MAY* be used to add attributes to > properties but for many other things too. >
The article *you* reference about attributes lists C# attributes as the *first* example. And defines an attribute as: "For clarity, attributes should more correctly be considered metadata <https://en.wikipedia.org/wiki/Metadata>. An attribute is frequently and generally a property of a property." HHVM uses the name attribute, for the same syntax that Dimitry proposes here. *MUST* is such a strong word in this context, this is not an 0 XOR 1 issue.

Fleshgrinder

10 years ago
On 4/24/2016 1:58 PM, Benjamin Eberlei wrote:
> The article *you* reference about attributes lists C# attributes as the > *first* example. > > And defines an attribute as: > > "For clarity, attributes should more correctly be considered metadata > <https://en.wikipedia.org/wiki/Metadata>. An attribute is frequently and > generally a property of a property." > > HHVM uses the name attribute, for the same syntax that Dimitry proposes > here. > > *MUST* is such a strong word in this context, this is not an 0 XOR 1 issue. >
On 4/24/2016 2:07 PM, Zeev Suraski wrote:
> Thanks for the programming 101 lesson :) > > I pointed out to Sebastian, who suggested that 'attributes' are
equivalent to 'member variables' or 'properties', that they're very consistently referred to in PHP as 'properties' and that I'm not aware that they were ever referred to as 'attributes'.
> > Nothing in your links suggested otherwise, arguably the opposite.
According to https://en.wikipedia.org/wiki/Attribute_(computing), "An attribute is frequently and generally a property of a property", and while "the term attribute can and is often treated as equivalent to a property depending on the technology being discussed", when the technology in question is PHP, this is not the case.
> > Of course, member visibility modifiers and access modifiers
(public/protected/private, static, etc.) are a different thing. Saying member variables are referred to as attributes (incorrect in the context of PHP, AFAIK) is very different from saying these modifiers are referred to as attributes (correct and sensible).
> > Last, we're absolutely allowed to have our own definitions of terms as
long as they're sensible. Both attributes and annotations are sensible here, and the fact there may be languages that treat attributes as equivalent to properties isn't a strong reason against using that term for something sensible, that is different from these other languages, in PHP.
> > Zeev >
As I said, unless somebody knows an even better term. It also depends on which source code is going to be allowed to have this kind of additional metadata. If we limit it to functions, classes, properties, and methods: /all good./ Why? We might want to allow adding of metadata to other parts of the source code in the future. I already asked regarding annotating files themselves (as is possible with PhpDoc's file-level docblock) and then it becomes complicated. I am not saying we need this, I am saying we need to clarify this. Using the term attribute without defining what the term means is problematic. I know, Facebook/HHVM/Hack but we are not required to be compliant with them and they are not our benchmark. Using the term annotation is not so problematic because it has a clear definition (not only in computer science) and it does not say anything about the applicability of what it does, it only states the functionality of the feature itself: /adding metadata to data/. You want to avoid writing extensive definitions of terms that are going to be used since people then need to learn these terms. Just think about the normal usage of the word attribute in language, e.g. describe the attributes of this image. The answer could be "it shows a woman with long hair and blue eyes". Hence, /long hair/ and /glue eyes/ are attributes of the woman. However, asking for the annotations of this image would result in a different answer, the asked person, assuming she knows what the word in general means, turns the image and would tell us the date and time it was taken or printed, or maybe the ISO level, ... whatever. Is *public* an attribute of the property? /Yes/ Is *static* an attribute of the method? /Yes/ Is *name* an attribute of a person? /Yes/ Is *__toString* an attribute of the class? /Yes, behavioral/ Is *inattentive* an attribute of the child? /Yes/ ... So, what should `$r->getAttributes()` return? It is to broad, it is to generic, it is too often mixed up. `$r->getAnnotations()` is less ambiguous in the context of programming and thus a better choice. I hope you can follow my /Gedankenspiel/ (mind game) better now. :)
-- Richard "Fleshgrinder" Fussenegger

Fleshgrinder

10 years ago
Another way to illustrate what I mean: The feature will be used /to annotate/ (add metadata) and the so /annotated/ data has additional /attributes/ afterwards. Trying building the sentence if the feature is called /attributes/. @entity @invariant final class A { @inject private static $x; @test public memoized function f(){} } r = reflect A r.getModifiers = [ final ] r.getAnnotations = [ entity, invariant ] r.getAttributes = [ final, entity, invariant ] r = reflect A.x r.getModifiers = [ private, static ] r.getAnnotations = [ inject ] r.getAttributes = [ private, static, inject ] r = reflect A.f r.getModifiers = [ public, memoized ] r.getAnnotations = [ test ] r.getAttributes = [ public, memoized, test ] Note that there are programming languages that allow adding of metadata solely via annotations: Ceylon. No matter the data to annotate. Hence, in Ceylon no differentiation would be made between modifiers and annotations. However, one could check the actual annotation type to determine what it is. https://modules.ceylon-lang.org/repo/1/ceylon/language/1.2.2/module-doc/api/index.html#section-annotations
-- Richard "Fleshgrinder" Fussenegger

Fleshgrinder

10 years ago
On 4/24/2016 2:54 PM, Fleshgrinder wrote:
> Another way to illustrate what I mean: > > The feature will be used /to annotate/ (add metadata) and the so > /annotated/ data has additional /attributes/ afterwards. > > Trying building the sentence if the feature is called /attributes/. > > @entity > @invariant > final class A { > > @inject > private static $x; > > @test > public memoized function f(){} > > } > > r = reflect A > r.getModifiers = [ final ] > r.getAnnotations = [ entity, invariant ] > r.getAttributes = [ final, entity, invariant ] > > r = reflect A.x > r.getModifiers = [ private, static ] > r.getAnnotations = [ inject ] > r.getAttributes = [ private, static, inject ] > > r = reflect A.f > r.getModifiers = [ public, memoized ] > r.getAnnotations = [ test ] > r.getAttributes = [ public, memoized, test ] > > Note that there are programming languages that allow adding of metadata > solely via annotations: Ceylon. No matter the data to annotate. Hence, > in Ceylon no differentiation would be made between modifiers and > annotations. However, one could check the actual annotation type to > determine what it is. > > https://modules.ceylon-lang.org/repo/1/ceylon/language/1.2.2/module-doc/api/index.html#section-annotations >
The example isn't actually complete, sorry: @entity @invariant final class A { @inject private static $x = 0; @test public memoized function f(){} } r = reflect A r.getModifiers = [ final ] r.getAnnotations = [ entity, invariant ] r.getAttributes = [ final, entity, invariant, $x, f() ] r = reflect A.x r.getModifiers = [ private, static ] r.getAnnotations = [ inject ] r.getAttributes = [ private, static, inject, value((int) 0) ] r = reflect A.f() r.getModifiers = [ public, memoized ] r.getAnnotations = [ test ] r.getAttributes = [ public, memoized, test ] I hope I didn't forgot any attribute. :P All in all, attributes are things that things have to describe those things and annotations allows us to add additional attributes to things, on top of possible other attributes that are already addable by other means (modifiers, values, ...).
-- Richard "Fleshgrinder" Fussenegger

Zeev Suraski

10 years ago
> -----Original Message----- > From: Fleshgrinder [mailto:php@fleshgrinder.com] > Sent: Sunday, April 24, 2016 2:49 PM > To: Zeev Suraski <zeev@zend.com>; Sebastian Bergmann > <sebastian@php.net> > Cc: internals@lists.php.net > Subject: Re: [PHP-DEV] [RFC] PHP Attributes > > On 4/24/2016 1:00 PM, Zeev Suraski wrote: > > > > > >> -----Original Message----- > >> From: Sebastian Bergmann [mailto:sebastian@php.net] > >> Sent: Sunday, April 24, 2016 12:14 PM > >> To: internals@lists.php.net > >> Subject: Re: [PHP-DEV] [RFC] PHP Attributes > >> > >> On 04/21/2016 11:13 PM, Dmitry Stogov wrote: > >>> I would like to present an RFC proposing support for native annotation. > >> > >> Dmitry, > >> > >> please use "annotation" as the name for this feature and not "attribute". > >> > >> We already have attributes: it's what we use to store data in > >> objects. And while some people call them "member variables" or > >> "properties" the correct term for them (AFAIK) is "attribute". > > > > I don't think I've ever heard properties referred to as attributes in the > context of PHP. The PHP manual defines them and refers to them throughout > as 'properties'. > > > > (I'm not voicing an opinion on attributes vs. annotations; Just pointing out > that data storage inside objects isn't named attributes). > > > > Zeev > > > > The terminology here is pretty clear and you can just look it up, it has nothing > to do with PHP or Java or whatever. The main problem is simply that people > do not know the terminology and get mixed up. That is usually okay in a > casual discussion where each peer understands what the other is referring to. > However, we are dealing with language specification here and need to be very > exact or we fuck it up for the users. > > `$x` is a property in the following example: > > class A { > $x; > } > > https://en.wikipedia.org/wiki/Property_%28programming%29
Richard, Thanks for the programming 101 lesson :) I pointed out to Sebastian, who suggested that 'attributes' are equivalent to 'member variables' or 'properties', that they're very consistently referred to in PHP as 'properties' and that I'm not aware that they were ever referred to as 'attributes'. Nothing in your links suggested otherwise, arguably the opposite. According to https://en.wikipedia.org/wiki/Attribute_(computing), "An attribute is frequently and generally a property of a property", and while "the term attribute can and is often treated as equivalent to a property depending on the technology being discussed", when the technology in question is PHP, this is not the case. Of course, member visibility modifiers and access modifiers (public/protected/private, static, etc.) are a different thing. Saying member variables are referred to as attributes (incorrect in the context of PHP, AFAIK) is very different from saying these modifiers are referred to as attributes (correct and sensible). Last, we're absolutely allowed to have our own definitions of terms as long as they're sensible. Both attributes and annotations are sensible here, and the fact there may be languages that treat attributes as equivalent to properties isn't a strong reason against using that term for something sensible, that is different from these other languages, in PHP. Zeev

Dan Ackroyd

10 years ago
On 21 April 2016 at 22:13, Dmitry Stogov <dmitry@zend.com> wrote:
> Hi, > > > I would like to present an RFC proposing support for native annotation. >
Hi Dmitry, Although everyone will have an opinion about the syntax, I think there is one criticism that should be thought about; the chosen syntax isn't future expandable to other concerns. People have talked about "design by contract" RFCs where annotation like data is readable by the engine. The current proposed syntax _could_ be used to store DBC data, but it would not be clear what would be annotation data to read by the application, and what was DBC data to be read by the engine. Changing the proposed syntax to be something like @attr(...) would allow it to be expanded in the future by adding a @contract(...) construct: @attr(test($a + $b > 0)) // This is attribute data read by userland code @contract(require($a + $b > 0, 'InvalidFooArgsException')) // This is DBC data read by the engine. function foo($a, $b) { ... } Making features be compatible with future features would avoid a lot of potential pain down the road. cheers Dan

Levi Morrison

10 years ago
On Sun, Apr 24, 2016 at 10:03 AM, Dan Ackroyd <danack@basereality.com> wrote:
> On 21 April 2016 at 22:13, Dmitry Stogov <dmitry@zend.com> wrote: >> Hi, >> >> >> I would like to present an RFC proposing support for native annotation. >> > > Hi Dmitry, > > Although everyone will have an opinion about the syntax, I think there > is one criticism that should be thought about; the chosen syntax isn't > future expandable to other concerns. > > People have talked about "design by contract" RFCs where annotation > like data is readable by the engine. The current proposed syntax > _could_ be used to store DBC data, but it would not be clear what > would be annotation data to read by the application, and what was DBC > data to be read by the engine. > > Changing the proposed syntax to be something like @attr(...) would > allow it to be expanded in the future by adding a @contract(...) > construct: > > @attr(test($a + $b > 0)) // This is attribute data read by userland code > @contract(require($a + $b > 0, 'InvalidFooArgsException')) // This is > DBC data read by the engine. > function foo($a, $b) { > ... > } > > Making features be compatible with future features would avoid a lot > of potential pain down the road.
Genuine question[1]: how is @attr() different than `<<attr()>>`? Also, isn't `@attr()` 100% valid user-land code today that can precede function or constant declarations? [1] I don't like that I have to make that explicit but it is what it is.

Fleshgrinder

10 years ago
On 4/24/2016 6:57 PM, Levi Morrison wrote:
> Genuine question[1]: how is @attr() different than `<<attr()>>`? Also, > isn't `@attr()` 100% valid user-land code today that can precede > function or constant declarations? > > [1] I don't like that I have to make that explicit but it is what it is. >
ROFL, that is actually totally true and I never thought about it. Damn silencing operator! :P However, i still think that the brackets are unnecessary and make it look like function calls. <<annotation values>> function f() {}
-- Richard "Fleshgrinder" Fussenegger

Dan Ackroyd

10 years ago
Hey Levi, TL:DR, if we can reserve some syntax for annotations that will be used by the PHP Engine, that would cover my concern. On 24 April 2016 at 17:57, Levi Morrison <levim@php.net> wrote:
> isn't `@attr()` 100% valid user-land code today that can precede
Er, yes it is! I didn't put too much thought the suggestion. The point I was trying to make is that it explicitly says that "this is a userland attribute", rather than using the generic <<...>> syntax, which we might want to use for non-userland attributes at some point.
> how is @attr() different than `<<attr()>>`?
If this RFC passes, someone could implement their own DBC feature using an annotation similar to: <<dbc($a + $b > 0, 'InvalidFooArgsException')>> Which under the current RFC proposal, would just be a standard annotation as far the PHP Engine is concerned. The framework/application the person is using would do the work to implement the DBC feature in userland code. In two years time when we want to implement dbc in the Engine, we would need to choose between either: * using <<dbc(...)>> as the syntax for the PHP internal feature, which would break userland code where they had chosen the same name we had. * using a different syntax to the <<...>> one to implement a DBC feature. * think up a cunning way of adding to the <<...>> syntax that doesn't break existing code that uses anotations. Choosing a syntax now that can be expanded for other uses in the future, is easier to do now, rather than in two years time, when it would be a BC break. Hack has implemented some 'special' attributes[1], that are interpreted by the HHVM Engine. All of those special attributes start with double underscores. If we did the same, and reserved (through documentation), that annotations starting with leading double underscores are reserved for future expansion, that would allow us to implement them (if we wanted to) without too many complaints of BC breakage. i.e. <<dbc(...)>> // this is a userland implementation <<__dbc(...)>> // Reserved for PHP internal implementation. cheers Dan [1] https://docs.hhvm.com/hack/attributes/special

Rowan Collins

10 years ago
Dan Ackroyd wrote on 24/04/2016 21:45:
> The point > I was trying to make is that it explicitly says that "this is a > userland attribute", rather than using the generic <<...>> syntax, > which we might want to use for non-userland attributes at some point.
I think framing the problem as "engine vs userland" is misleading; what you are actually saying is "how do we avoid name collisions?" There's currently no difference in syntax between userland and engine functions and classes, and *that is a good thing*, because it means you can transparently apply polyfills, create C / Zephir extensions to replace performance-sensitive functions, etc. The same could be done for annotations - if it's possible to create a userland DBC library, then why shouldn't it be able to polyfill the same syntax as an engine one? As mentioned elsewhere, in an OO implementation of annotations, the existing namespace hierarchy could be used (which includes the \php\ reservation, which we've never taken advantage of). If it's not, we are left with naming conventions. I would suggest that even if we don't have objects representing the annotations, "\" should be allowed, and encouraged, in their names. Thus, *by convention*, attributes would be prefixed with at least a vendor name ("<<Doctrine\Foo>>", etc). PHP could reserve "__" and "php\" for future internal use, but also semi-reserve the global namespace, as described here: http://php.net/manual/en/userlandnaming.rules.php
> PHP owns the top-level namespace but tries to find decent descriptive
names and avoid any obvious clashes. If somebody creates a userland library that uses <<DBC\Require(...)>>, it's up to them to deal with the consequences if PHP later implements an incompatible version, just as it would be if they wrote a function called dbc_require(), or DBC::require(). Regards,
-- Rowan Collins [IMSoP]

Dmitry Stogov

10 years ago
On 04/24/2016 07:57 PM, Levi Morrison wrote:
> On Sun, Apr 24, 2016 at 10:03 AM, Dan Ackroyd <danack@basereality.com> wrote: >> On 21 April 2016 at 22:13, Dmitry Stogov <dmitry@zend.com> wrote: >>> Hi, >>> >>> >>> I would like to present an RFC proposing support for native annotation. >>> >> Hi Dmitry, >> >> Although everyone will have an opinion about the syntax, I think there >> is one criticism that should be thought about; the chosen syntax isn't >> future expandable to other concerns. >> >> People have talked about "design by contract" RFCs where annotation >> like data is readable by the engine. The current proposed syntax >> _could_ be used to store DBC data, but it would not be clear what >> would be annotation data to read by the application, and what was DBC >> data to be read by the engine. >> >> Changing the proposed syntax to be something like @attr(...) would >> allow it to be expanded in the future by adding a @contract(...) >> construct: >> >> @attr(test($a + $b > 0)) // This is attribute data read by userland code >> @contract(require($a + $b > 0, 'InvalidFooArgsException')) // This is >> DBC data read by the engine. >> function foo($a, $b) { >> ... >> } >> >> Making features be compatible with future features would avoid a lot >> of potential pain down the road. > Genuine question[1]: how is @attr() different than `<<attr()>>`? Also, > isn't `@attr()` 100% valid user-land code today that can precede > function or constant declarations?
@attr() - is a valid "silenced" call to function named "attr". This syntax can't be reused. Thanks. Dmitry.

Stas Malyshev

10 years ago
Hi!
> @attr() - is a valid "silenced" call to function named "attr". > This syntax can't be reused.
Not valid if it's in the middle of class definition, and not valid in form of: @attr() function foo() { ... } This is not a valid PHP syntax now. So I'm not sure why it can't be used. << is an operator too and so is >> .
-- Stas Malyshev smalyshev@gmail.com

Rowan Collins

10 years ago
On 25/04/2016 19:37, Stanislav Malyshev wrote:
> Hi! > >> @attr() - is a valid "silenced" call to function named "attr". >> This syntax can't be reused. > Not valid if it's in the middle of class definition, and not valid in > form of: > > @attr() function foo() { ... } > > This is not a valid PHP syntax now. So I'm not sure why it can't be > used. << is an operator too and so is >> . >
No, but this is valid: @atrr(); function foo() { ... } That's perhaps a little too close for comfort...? Regards,
-- Rowan Collins [IMSoP]

Stas Malyshev

10 years ago
Hi!
> No, but this is valid: > > @atrr(); function foo() { ... } > > That's perhaps a little too close for comfort...?
That's different syntax. If you put ; in the middle of statement, it can change - "$c = $a + $b;" is not the same as "$c = $a; + $b;" - but nobody thinks + can not be used because of that. As I said, << and >> are existing operators too, so if you are creative enough, I'm sure you can find cases like that too.
-- Stas Malyshev smalyshev@gmail.com

Dmitry Stogov

10 years ago
On 04/25/2016 11:20 PM, Stanislav Malyshev wrote:
> Hi! > >> No, but this is valid: >> >> @atrr(); function foo() { ... } >> >> That's perhaps a little too close for comfort...? > That's different syntax. If you put ; in the middle of statement, it can > change - "$c = $a + $b;" is not the same as "$c = $a; + $b;" - but > nobody thinks + can not be used because of that. As I said, << and >> > are existing operators too, so if you are creative enough, I'm sure you > can find cases like that too. >
Hi Stas, You may try to replace attribute syntax with @attr(...) (without semicolon) into our PHP parser. Note that we have LALR grammar + restrictions caused by semantic actions. If you are able to do this, I'll add it into the RFC as an option. Thanks. Dmitry.

Stas Malyshev

10 years ago
Hi!
> You may try to replace attribute syntax with @attr(...) (without > semicolon) into our PHP parser. > Note that we have LALR grammar + restrictions caused by semantic actions. > If you are able to do this, I'll add it into the RFC as an option.
I'll try if I have time soon.
-- Stas Malyshev smalyshev@gmail.com

Fleshgrinder

10 years ago
On 4/25/2016 10:04 PM, Rowan Collins wrote:
> On 25/04/2016 19:37, Stanislav Malyshev wrote: >> Hi! >> >>> @attr() - is a valid "silenced" call to function named "attr". >>> This syntax can't be reused. >> Not valid if it's in the middle of class definition, and not valid in >> form of: >> >> @attr() function foo() { ... } >> >> This is not a valid PHP syntax now. So I'm not sure why it can't be >> used. << is an operator too and so is >> . >> > > No, but this is valid: > > @atrr(); function foo() { ... } > > That's perhaps a little too close for comfort...? > > Regards, >
Actually I agree with Stanislav (in pretty much every point he made so far) on the syntax discussion, however, I already said that the brackets are the main problem and it seems as it nobody sees it but your example illustrates it perfectly. @fopen('file', 'r'); function foo(){} @deprecated function foo(){} @throws InvalidArgumentException function foo(){} @route ['name' => 'foo'] function foo(){} Leaving out the brackets makes it very clear. I am in an extreme rush and will read more in the thread the next days, sorry.
-- Richard "Fleshgrinder" Fussenegger

Dmitry Stogov

10 years ago
On 04/24/2016 07:03 PM, Dan Ackroyd wrote:
> On 21 April 2016 at 22:13, Dmitry Stogov <dmitry@zend.com> wrote: >> Hi, >> >> >> I would like to present an RFC proposing support for native annotation. >> > Hi Dmitry, > > Although everyone will have an opinion about the syntax, I think there > is one criticism that should be thought about; the chosen syntax isn't > future expandable to other concerns. > > People have talked about "design by contract" RFCs where annotation > like data is readable by the engine. The current proposed syntax > _could_ be used to store DBC data, but it would not be clear what > would be annotation data to read by the application, and what was DBC > data to be read by the engine. > > Changing the proposed syntax to be something like @attr(...) would > allow it to be expanded in the future by adding a @contract(...) > construct: > > @attr(test($a + $b > 0)) // This is attribute data read by userland code > @contract(require($a + $b > 0, 'InvalidFooArgsException')) // This is > DBC data read by the engine. > function foo($a, $b) { > ... > } > > Making features be compatible with future features would avoid a lot > of potential pain down the road. > > cheers > Dan
"@..." syntax can't work. @ - is a silence operator. <<Contract\require($a + $b > 0, 'InvalidFooArgsException')>> - this is not really worse than your example. I know, any syntax is going to be loved by ones and hated by others... It's not possible to satisfy everyone. Thanks. Dmitry.

Lester Caine

10 years ago
On 25/04/16 09:42, Dmitry Stogov wrote:
> I know, any syntax is going to be loved by ones and hated by others... > It's not possible to satisfy everyone.
If one takes a step back, current well used annotation IS provided but not cleanly documented in the docbloc material that is a major element of legacy code. There is clearly a case for improving the usage of this style of working, and adding 'attributes' to the rest of the rest of this annotation seems to me to be the best way forward for a STOCK PHP install. Every current IDE I've seen will correctly display docbloc annotation and has the option to disable tags in that code if required. I'm looking at the <?hh examples in hack and thinking that this is an example of where an 'hack compatibility' extension to PHP may have a place, but adoption some hack rules such as <<>> then imples that <? code should work the same as <?hh code. That does not seem the right way forward for users who have no intention to use hack and have no access to it on their hosting? More important, and subtle differences will cause even more problems? So can we not maintain a more flexible roadmap that cherry picking other languages directly?
-- Lester Caine - G8HFL ----------------------------- Contact - http://lsces.co.uk/wiki/?page=contact L.S.Caine Electronic Services - http://lsces.co.uk EnquirySolve - http://enquirysolve.com/ Model Engineers Digital Workshop - http://medw.co.uk Rainbow Digital Media - http://rainbowdigitalmedia.co.uk

Stas Malyshev

10 years ago
Hi!
> https://wiki.php.net/rfc/attributes
I've read the proposal and I like the idea of reviving the attributes idea and the simplicity of the approach. I have the following questions for it: - Namespacing. Since the attribute names are not processed in any way according to spec, it may be rather hard to ensure attribute requirements from different domains do not conflict, and we do not get into the same problem we've got into with class names before we implemented namespaces. How it is planned to address the issue? - The expressions support is nice, however I'm not sure I understand what PHP application is supposed to do with syntax trees? I.e. taking the example in the RFC, if I got AST\Node representing <<test($a + $b > 0)>>, how do I use it? Would I implement mini-PHP-engine in PHP to evaluate such an expression? Am I supposed to use some other functionality to do it? It is unclear. I don't think promoting reimplementing PHP expressions in a myriad of different ways (which all would be subtly different and distinct from how PHP understands such expressions) is not good, and if we expose those trees we should also provide means to work with them. Otherwise any advanced usage of the feature would mean basically reimplementing PHP engine in PHP. As a side note, my personal opinion is that for about 90% of use cases expressions are not needed. For 90% of those which are needed, string + some form of eval would cover it's use case since all the'd be doing is evaluating the expression and checking the resulting value. - As far as I can see, AST extension has no documentation except for initial README. If it is going to end up in core, this needs to be fixed. Preferably before it is merged into core. - I would also consider moving AST namespace under \PHP since we have this one reserved (unlike \AST which we did not reserve). - Are attributes also allowed to apply to anonymous classes/functions? Would the syntax still work in that case? _ I also think <<...>> syntax is ugly and @attribute syntax would be much better (and also is used in Java attributes and Python decorators). That's my personal opinion though. Thanks,
-- Stas Malyshev smalyshev@gmail.com

Dmitry Stogov

10 years ago
On 04/25/2016 06:23 AM, Stanislav Malyshev wrote:
> Hi! > >> https://wiki.php.net/rfc/attributes > I've read the proposal and I like the idea of reviving the attributes > idea and the simplicity of the approach. > > I have the following questions for it: > > - Namespacing. Since the attribute names are not processed in any way > according to spec, it may be rather hard to ensure attribute > requirements from different domains do not conflict, and we do not get > into the same problem we've got into with class names before we > implemented namespaces. How it is planned to address the issue?
I'm going to extend RFC with namespace support. Attribute name is going to be qualified string.
> > - The expressions support is nice, however I'm not sure I understand > what PHP application is supposed to do with syntax trees? I.e. taking > the example in the RFC, if I got AST\Node representing <<test($a + $b > > 0)>>, how do I use it? Would I implement mini-PHP-engine in PHP to > evaluate such an expression? Am I supposed to use some other > functionality to do it? It is unclear. > I don't think promoting reimplementing PHP expressions in a myriad of > different ways (which all would be subtly different and distinct from > how PHP understands such expressions) is not good, and if we expose > those trees we should also provide means to work with them. Otherwise > any advanced usage of the feature would mean basically reimplementing > PHP engine in PHP.
AST is going to be processed and evaluated using php-ast extension. Nikita was going to propose including it into core distribution.
> As a side note, my personal opinion is that for about 90% of use cases > expressions are not needed. For 90% of those which are needed, string + > some form of eval would cover it's use case since all the'd be doing is > evaluating the expression and checking the resulting value.
This is a question. We may use AST expression or just strings. In second case we miss syntax verification at compile time. I'm going to describe the problem in RFC and set an additional voting question.
> > - As far as I can see, AST extension has no documentation except for > initial README. If it is going to end up in core, this needs to be > fixed. Preferably before it is merged into core. > > - I would also consider moving AST namespace under \PHP since we have > this one reserved (unlike \AST which we did not reserve).
For me AST is the best choice here.
> > - Are attributes also allowed to apply to anonymous classes/functions? > Would the syntax still work in that case?
No. At least now.
> > _ I also think <<...>> syntax is ugly and @attribute syntax would be > much better (and also is used in Java attributes and Python decorators). > That's my personal opinion though.
@ is reserved for silence operator. Thanks. Dmitry.