[PHP-DEV] [RFC] Simple Annotations
Dear Internals, I'm announcing a simplified RFC for annotations: https://wiki.php.net/rfc/simple-annotations It's an alternative to the proposed Attributes RFC and the (2010) Annotations RFC. I'm attempting with this to describe a feature that is closer to the language than what is proposed by the Attributes RFC, by permitting the use of any valid PHP expression as an annotation value. Where the Attributes RFC proposes new syntax for what is essentially arrays, this proposal instead permits you to use actual arrays, e.g. without inventing any new syntax. It also allows you to use values of any other type, including objects. This makes the proposed feature more immediately useful, with a minimal learning curve. Compared with the old Annotations RFC (and Doctrine Annotations, etc.) this proposal does not attempt to define or enforce any rules about what annotations are, permitted usage, inheritance rules, etc. - instead it adds a very simple language feature upon which the community may choose to build (and evolve) facilities that implement additional rules and logic. My hope is that, if we can agree on something very small and simple, we can defer the more opinionated discussions about rules and logic to the community. In my opinion, language features should be simple, and consistent with the language - I believe the way to do that, is to avoid discussions about how such as facility should be used, and instead focus on how it should work. There is a lot of opinion built into the old Annotations RFC, and into Doctrine - features that attempt to dictate how the feature should be used. I don't believe the language should dictate what is or isn't correct or appropriate use. Please review and comment. Thanks, Rasmus Schultz -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] Simple Annotations
On Fri, May 13, 2016 at 2:11 PM, Rasmus Schultz wrote: > Dear Internals, > > I'm announcing a simplified RFC for annotations: > > https://wiki.php.net/rfc/simple-annotations > > It's an alternative to the proposed Attributes RFC and the (2010) > Annotations RFC. > > I'm attempting with this to describe a feature that is closer to the > language than what is proposed by the Attributes RFC, by permitting > the use of any valid PHP expression as an annotation value. > > Where the Attributes RFC proposes new syntax for what is essentially > arrays, this proposal instead permits you to use actual arrays, e.g. > without inventing any new syntax. It also allows you to use values of > any other type, including objects. This makes the proposed feature > more immediately useful, with a minimal learning curve. > > Compared with the old Annotations RFC (and Doctrine Annotations, etc.) > this proposal does not attempt to define or enforce any rules about > what annotations are, permitted usage, inheritance rules, etc. - > instead it adds a very simple language feature upon which the > community may choose to build (and evolve) facilities that implement > additional rules and logic. > > My hope is that, if we can agree on something very small and simple, > we can defer the more opinionated discussions about rules and logic to > the community. > > In my opinion, language features should be simple, and consistent with > the language - I believe the way to do that, is to avoid discussions > about how such as facility should be used, and instead focus on how it > should work. There is a lot of opinion built into the old Annotations > RFC, and into Doctrine - features that attempt to dictate how the > feature should be used. I don't believe the language should dictate > what is or isn't correct or appropriate use. I think I like this, however a couple of simple questions: 1) Any chance for (optionally) _naming_ annotations? It would be nice to be able to do: ->getAnnotation('table') and not have to handle unnecessary annotations 2) I like the idea of Hacks memoize for example, how do you see those being implemented? Or don't you? 3) When are annotations executed? On ->getAnnotations()? Every time? Thanks,
Re: [PHP-DEV] [RFC] Simple Annotations
On 05/13/2016 02:57 PM, Davey Shafik wrote: > On Fri, May 13, 2016 at 2:11 PM, Rasmus Schultz wrote: > >> Dear Internals, >> >> I'm announcing a simplified RFC for annotations: >> >> https://wiki.php.net/rfc/simple-annotations >> >> It's an alternative to the proposed Attributes RFC and the (2010) >> Annotations RFC. >> >> I'm attempting with this to describe a feature that is closer to the >> language than what is proposed by the Attributes RFC, by permitting >> the use of any valid PHP expression as an annotation value. >> >> Where the Attributes RFC proposes new syntax for what is essentially >> arrays, this proposal instead permits you to use actual arrays, e.g. >> without inventing any new syntax. It also allows you to use values of >> any other type, including objects. This makes the proposed feature >> more immediately useful, with a minimal learning curve. >> >> Compared with the old Annotations RFC (and Doctrine Annotations, etc.) >> this proposal does not attempt to define or enforce any rules about >> what annotations are, permitted usage, inheritance rules, etc. - >> instead it adds a very simple language feature upon which the >> community may choose to build (and evolve) facilities that implement >> additional rules and logic. >> >> My hope is that, if we can agree on something very small and simple, >> we can defer the more opinionated discussions about rules and logic to >> the community. >> >> In my opinion, language features should be simple, and consistent with >> the language - I believe the way to do that, is to avoid discussions >> about how such as facility should be used, and instead focus on how it >> should work. There is a lot of opinion built into the old Annotations >> RFC, and into Doctrine - features that attempt to dictate how the >> feature should be used. I don't believe the language should dictate >> what is or isn't correct or appropriate use. > > I think I like this, however a couple of simple questions: > > 1) Any chance for (optionally) _naming_ annotations? It would be nice to be > able to do: ->getAnnotation('table') and not have to handle unnecessary > annotations not sure you need that, you can do this instead: ->getAnnotations(Table::class) however it won't throw an error if you have more than one "Table::class annotation" whereas I supposed you would expect so with a named annotation > > 2) I like the idea of Hacks memoize for example, how do you see those being > implemented? Or don't you? if Rasmus goal is to that "we can agree on something very small and simple" I guess it would best be left for another rfc > > 3) When are annotations executed? On ->getAnnotations()? Every time? given they are context free, I suppose they would be executed on the first call only but what about this kind of annotation classes? : class Counter { private static $i = 0; private $n; function __construct() { $this->n = self::$i++; } function get() { return $this->n; } } both the getAnnotations() calls order and whether or not annotations are executed every time would make a difference > > Thanks, > -- Mathieu Rochette
Re: [PHP-DEV] [RFC] Simple Annotations
Love the simplicity of the RFC, but I can already see people doing terrible things with it: << new DateTimeImmutable() >> << log(get_instance('logger')) >> And other global-state related stuff. TBH, I'd rather just allow a constant array (with constant expressions only), and that would be good enough :-) Cheers, Marco Pivetta http://twitter.com/Ocramius http://ocramius.github.com/ On 13 May 2016 at 15:15, Mathieu Rochette wrote: > > > On 05/13/2016 02:57 PM, Davey Shafik wrote: > > On Fri, May 13, 2016 at 2:11 PM, Rasmus Schultz > wrote: > > > >> Dear Internals, > >> > >> I'm announcing a simplified RFC for annotations: > >> > >> https://wiki.php.net/rfc/simple-annotations > >> > >> It's an alternative to the proposed Attributes RFC and the (2010) > >> Annotations RFC. > >> > >> I'm attempting with this to describe a feature that is closer to the > >> language than what is proposed by the Attributes RFC, by permitting > >> the use of any valid PHP expression as an annotation value. > >> > >> Where the Attributes RFC proposes new syntax for what is essentially > >> arrays, this proposal instead permits you to use actual arrays, e.g. > >> without inventing any new syntax. It also allows you to use values of > >> any other type, including objects. This makes the proposed feature > >> more immediately useful, with a minimal learning curve. > >> > >> Compared with the old Annotations RFC (and Doctrine Annotations, etc.) > >> this proposal does not attempt to define or enforce any rules about > >> what annotations are, permitted usage, inheritance rules, etc. - > >> instead it adds a very simple language feature upon which the > >> community may choose to build (and evolve) facilities that implement > >> additional rules and logic. > >> > >> My hope is that, if we can agree on something very small and simple, > >> we can defer the more opinionated discussions about rules and logic to > >> the community. > >> > >> In my opinion, language features should be simple, and consistent with > >> the language - I believe the way to do that, is to avoid discussions > >> about how such as facility should be used, and instead focus on how it > >> should work. There is a lot of opinion built into the old Annotations > >> RFC, and into Doctrine - features that attempt to dictate how the > >> feature should be used. I don't believe the language should dictate > >> what is or isn't correct or appropriate use. > > > > I think I like this, however a couple of simple questions: > > > > 1) Any chance for (optionally) _naming_ annotations? It would be nice to > be > > able to do: ->getAnnotation('table') and not have to handle unnecessary > > annotations > not sure you need that, you can do this instead: > ->getAnnotations(Table::class) > however it won't throw an error if you have more than one "Table::class > annotation" whereas I supposed you would expect so with a named annotation > > > > 2) I like the idea of Hacks memoize for example, how do you see those > being > > implemented? Or don't you? > if Rasmus goal is to that "we can agree on something very small and > simple" I guess it would best be left for another rfc > > > > 3) When are annotations executed? On ->getAnnotations()? Every time? > given they are context free, I suppose they would be executed on the > first call only > > but what about this kind of annotation classes? : > > class Counter { > private static $i = 0; > private $n; > function __construct() { > $this->n = self::$i++; > } > function get() { return $this->n; } > } > > both the getAnnotations() calls order and whether or not annotations are > executed every time would make a difference > > > > > Thanks, > > > > -- > Mathieu Rochette > >
Re: [PHP-DEV] [RFC] Simple Annotations
On Fri, May 13, 2016 at 2:11 PM, Rasmus Schultz wrote: > Dear Internals, > > I'm announcing a simplified RFC for annotations: > > https://wiki.php.net/rfc/simple-annotations > > It's an alternative to the proposed Attributes RFC and the (2010) > Annotations RFC. > > I'm attempting with this to describe a feature that is closer to the > language than what is proposed by the Attributes RFC, by permitting > the use of any valid PHP expression as an annotation value. > > Where the Attributes RFC proposes new syntax for what is essentially > arrays, this proposal instead permits you to use actual arrays, e.g. > without inventing any new syntax. It also allows you to use values of > any other type, including objects. This makes the proposed feature > more immediately useful, with a minimal learning curve. > The attributes RFC actually does not specific syntax or arrays, but either a scalar (number, string) or an ast\node. A proposal for arrays *would* actually be "closer to the language" as you describe it. Compared with the old Annotations RFC (and Doctrine Annotations, etc.) > this proposal does not attempt to define or enforce any rules about > what annotations are, permitted usage, inheritance rules, etc. - > instead it adds a very simple language feature upon which the > community may choose to build (and evolve) facilities that implement > additional rules and logic. > > My hope is that, if we can agree on something very small and simple, > we can defer the more opinionated discussions about rules and logic to > the community. > > In my opinion, language features should be simple, and consistent with > the language - I believe the way to do that, is to avoid discussions > about how such as facility should be used, and instead focus on how it > should work. There is a lot of opinion built into the old Annotations > RFC, and into Doctrine - features that attempt to dictate how the > feature should be used. I don't believe the language should dictate > what is or isn't correct or appropriate use. > > Please review and comment. I see you took my question about context from the other thread and defined annotations to be context less. You should add to the RFC examples of what is happening, if you do access context, for example access to undefined variable notices: <<$x>> class Foo {} $refl = new ReflectionClass('Foo'); var_dump($refl->getAttributes(Foo::class)); // PHP Notice: Undefined variable: x in Command line code on line 1 Still this is imho a weak point of your RFC. You already stated that a "simplified annotation" the way you see it is the expression wrapped in a closure: function () { return php-expression; } So if an annotation is a function/closure that belongs to its target (class, property, function, method, ...) then why wouldn't there be a way to specify arguments? The RFC should give an answer to that. The statement you add doesn't make sense without examples. How does dependency injection even work, when you don't have a context? "Annotations that do require context should explicitly ask for that context - for example, you could use an anonymous function, a callable, or an anonymous class, to provide context via dependency injection." greetings Benjamin > Thanks, > Rasmus Schultz > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >
Re: [PHP-DEV] [RFC] Simple Annotations
Thank you for your comments! Benjamin, I have removed the comment about annotation expressions executing in an empty closure, since this example was clearly confusing, and I have tried to clarify the fact that annotation expressions execute in an empty scope. To be clear, annotation expressions are *not* closures, and they do not accept arguments - there is no reason to complicate the concept of annotations by introducing an implied scope, when you can introduce one explicitly, for example: class Validation { private $validate; public function __construct(callable $validate) { $this->validate = $validate; } public function validate($instance) { return call_user_func($this->validate, $instance); } } << new Validation(function (User $user) { return false !== filter_var($user->email, FILTER_VALIDATE_EMAIL); }) >> class User { public $email; } Of course, you could just as well do this with an interface - as it the case with most examples I can think of. If you can think of a meaningful annotation that actually requires context and can't be implemented in a better/safer/cleaner way with abstraction, please post? I'd like to add an example, but really have never seen one that can't be solved better without annotations. Marco, > Love the simplicity of the RFC, but I can already see people doing terrible > things with it Every language lets you do terrible and great things - I don't believe in putting up roadblocks and complicating the language that prevent people from learning. > TBH, I'd rather just allow a constant array (with constant expressions only), > and that would be good enough :-) I don't agree with the idea of crippling a language feature to prevent beginners from making mistakes - when doing so would also prevent static analysis and block experienced developers from doing useful things with objects. Davey and Mathieu, Annotations can be "named" by using classes. Per the RFC, defining or enforcing a rule about allowing only one instance of the same type, is not a feature. Hack's memoize is an annotation for the language interpreter itself - that's beyond the scope of this RFC, but could be implemented in the future. Your third question is answered by the RFC here: > Annotation expressions are not evaluated until reflection is invoked, and are > evaluated only once and internally memoized upon the first call to > getAnnotations(). Thank you for your input so far! The RFC is 0.2 with the last updates based on your feedback - I would like to add a suitable case example for annotations that require context before I'd say this RFC is 1.0. If anyone can point at a real case example using Doctrine Annotations (or anything else) please do! Thanks, Rasmus On Sat, May 14, 2016 at 4:38 AM, Benjamin Eberlei wrote: > > > On Fri, May 13, 2016 at 2:11 PM, Rasmus Schultz wrote: >> >> Dear Internals, >> >> I'm announcing a simplified RFC for annotations: >> >> https://wiki.php.net/rfc/simple-annotations >> >> It's an alternative to the proposed Attributes RFC and the (2010) >> Annotations RFC. >> >> I'm attempting with this to describe a feature that is closer to the >> language than what is proposed by the Attributes RFC, by permitting >> the use of any valid PHP expression as an annotation value. >> >> Where the Attributes RFC proposes new syntax for what is essentially >> arrays, this proposal instead permits you to use actual arrays, e.g. >> without inventing any new syntax. It also allows you to use values of >> any other type, including objects. This makes the proposed feature >> more immediately useful, with a minimal learning curve. > > > The attributes RFC actually does not specific syntax or arrays, but either a > scalar (number, string) or an ast\node. > > A proposal for arrays *would* actually be "closer to the language" as you > describe it. > >> Compared with the old Annotations RFC (and Doctrine Annotations, etc.) >> this proposal does not attempt to define or enforce any rules about >> what annotations are, permitted usage, inheritance rules, etc. - >> instead it adds a very simple language feature upon which the >> community may choose to build (and evolve) facilities that implement >> additional rules and logic. >> >> My hope is that, if we can agree on something very small and simple, >> we can defer the more opinionated discussions about rules and logic to >> the community. >> >> In my opinion, language features should be simple, and consistent with >> the language - I believe the way to do that, is to avoid discussions >> about how such as facility should be used, and instead focus on how it >> should work. There is a lot of opinion built into the old Annotations >> RFC, and into Doctrine - features that attempt to dictate how the >> feature should be used. I don't believe the language should dictate >> what is or isn't correct or appropriate use. >> >> Please review and comment. > > > I see you took my question about context
Re: [PHP-DEV] [RFC] Simple Annotations
On 14 May 2016 at 12:48, Rasmus Schultz wrote: > > Hack's memoize is an annotation for the language interpreter itself - > that's beyond the scope of this RFC, but could be implemented in the > future. Please can you add something to the RFC that reserves the possibility to do this, so that it can be introduced in a BC compatible way in the future? Hand-waving away problems and saying that they aren't in scope is a sure-fire way to build deficiencies into a design. This is not a smaller problem in userland code, as it is easy for people to migrate from one library to another, but is almost impossible to overcome in core PHP, where any BC-breaking change causes lots of difficultly for end-users. cheers Dan -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] Simple Annotations
On Sat, May 14, 2016 at 1:48 PM, Rasmus Schultz wrote: > Thank you for your comments! > > Benjamin, > > I have removed the comment about annotation expressions executing in > an empty closure, since this example was clearly confusing, and I have > tried to clarify the fact that annotation expressions execute in an > empty scope. To be clear, annotation expressions are *not* closures, > and they do not accept arguments - there is no reason to complicate > the concept of annotations by introducing an implied scope, when you > can introduce one explicitly, for example: > > class Validation { > private $validate; > > public function __construct(callable $validate) { > $this->validate = $validate; > } > > public function validate($instance) { > return call_user_func($this->validate, $instance); > } > } > > << new Validation(function (User $user) { return false !== > filter_var($user->email, FILTER_VALIDATE_EMAIL); }) >> > class User { > public $email; > } > > Of course, you could just as well do this with an interface - as it > the case with most examples I can think of. > > If you can think of a meaningful annotation that actually requires > context and can't be implemented in a better/safer/cleaner way with > abstraction, please post? I'd like to add an example, but really have > never seen one that can't be solved better without annotations. > Every proposal before was about annotation/attributes as metadata for code. Your proposal makes them executable functions instead (nameless, attached to code blocks). I think this is just way out of scope of what the 80% want to achieve with this feature. > > Marco, > > > Love the simplicity of the RFC, but I can already see people doing > terrible things with it > > Every language lets you do terrible and great things - I don't believe > in putting up roadblocks and complicating the language that prevent > people from learning. > > > TBH, I'd rather just allow a constant array (with constant expressions > only), and that would be good enough :-) > > I don't agree with the idea of crippling a language feature to prevent > beginners from making mistakes - when doing so would also prevent > static analysis and block experienced developers from doing useful > things with objects. > > Davey and Mathieu, > > Annotations can be "named" by using classes. Per the RFC, defining or > enforcing a rule about allowing only one instance of the same type, is > not a feature. > > Hack's memoize is an annotation for the language interpreter itself - > that's beyond the scope of this RFC, but could be implemented in the > future. > > Your third question is answered by the RFC here: > > > Annotation expressions are not evaluated until reflection is invoked, > and are evaluated only once and internally memoized upon the first call to > getAnnotations(). > > Thank you for your input so far! > > The RFC is 0.2 with the last updates based on your feedback - I would > like to add a suitable case example for annotations that require > context before I'd say this RFC is 1.0. > > If anyone can point at a real case example using Doctrine Annotations > (or anything else) please do! > > Thanks, > Rasmus > > > On Sat, May 14, 2016 at 4:38 AM, Benjamin Eberlei > wrote: > > > > > > On Fri, May 13, 2016 at 2:11 PM, Rasmus Schultz > wrote: > >> > >> Dear Internals, > >> > >> I'm announcing a simplified RFC for annotations: > >> > >> https://wiki.php.net/rfc/simple-annotations > >> > >> It's an alternative to the proposed Attributes RFC and the (2010) > >> Annotations RFC. > >> > >> I'm attempting with this to describe a feature that is closer to the > >> language than what is proposed by the Attributes RFC, by permitting > >> the use of any valid PHP expression as an annotation value. > >> > >> Where the Attributes RFC proposes new syntax for what is essentially > >> arrays, this proposal instead permits you to use actual arrays, e.g. > >> without inventing any new syntax. It also allows you to use values of > >> any other type, including objects. This makes the proposed feature > >> more immediately useful, with a minimal learning curve. > > > > > > The attributes RFC actually does not specific syntax or arrays, but > either a > > scalar (number, string) or an ast\node. > > > > A proposal for arrays *would* actually be "closer to the language" as you > > describe it. > > > >> Compared with the old Annotations RFC (and Doctrine Annotations, etc.) > >> this proposal does not attempt to define or enforce any rules about > >> what annotations are, permitted usage, inheritance rules, etc. - > >> instead it adds a very simple language feature upon which the > >> community may choose to build (and evolve) facilities that implement > >> additional rules and logic. > >> > >> My hope is that, if we can agree on something very small and simple, > >> we can defer the more opinionated discussions about rules and logic to > >> the community. > >> > >> In my opinion, l
Re: [PHP-DEV] [RFC] Simple Annotations
Dan, I've added a note about special annotations to the "future scope" section, naming the memoization-annotation as an example. Benjamin, I don't think you mean "out of scope" of what 80% want, I think you mean "beyond the scope"? I think this will definitely cover what 80% want to achieve, and possibly a lot more - possibly new and useful patterns that we can't even predict. On Sat, May 14, 2016 at 3:41 PM, Benjamin Eberlei wrote: > > > On Sat, May 14, 2016 at 1:48 PM, Rasmus Schultz wrote: >> >> Thank you for your comments! >> >> Benjamin, >> >> I have removed the comment about annotation expressions executing in >> an empty closure, since this example was clearly confusing, and I have >> tried to clarify the fact that annotation expressions execute in an >> empty scope. To be clear, annotation expressions are *not* closures, >> and they do not accept arguments - there is no reason to complicate >> the concept of annotations by introducing an implied scope, when you >> can introduce one explicitly, for example: >> >> class Validation { >> private $validate; >> >> public function __construct(callable $validate) { >> $this->validate = $validate; >> } >> >> public function validate($instance) { >> return call_user_func($this->validate, $instance); >> } >> } >> >> << new Validation(function (User $user) { return false !== >> filter_var($user->email, FILTER_VALIDATE_EMAIL); }) >> >> class User { >> public $email; >> } >> >> Of course, you could just as well do this with an interface - as it >> the case with most examples I can think of. >> >> If you can think of a meaningful annotation that actually requires >> context and can't be implemented in a better/safer/cleaner way with >> abstraction, please post? I'd like to add an example, but really have >> never seen one that can't be solved better without annotations. > > > Every proposal before was about annotation/attributes as metadata for code. > Your proposal makes them executable functions instead (nameless, attached to > code blocks). > > I think this is just way out of scope of what the 80% want to achieve with > this feature. >> >> >> Marco, >> >> > Love the simplicity of the RFC, but I can already see people doing >> > terrible things with it >> >> Every language lets you do terrible and great things - I don't believe >> in putting up roadblocks and complicating the language that prevent >> people from learning. >> >> > TBH, I'd rather just allow a constant array (with constant expressions >> > only), and that would be good enough :-) >> >> I don't agree with the idea of crippling a language feature to prevent >> beginners from making mistakes - when doing so would also prevent >> static analysis and block experienced developers from doing useful >> things with objects. >> >> Davey and Mathieu, >> >> Annotations can be "named" by using classes. Per the RFC, defining or >> enforcing a rule about allowing only one instance of the same type, is >> not a feature. >> >> Hack's memoize is an annotation for the language interpreter itself - >> that's beyond the scope of this RFC, but could be implemented in the >> future. >> >> Your third question is answered by the RFC here: >> >> > Annotation expressions are not evaluated until reflection is invoked, >> > and are evaluated only once and internally memoized upon the first call to >> > getAnnotations(). >> >> Thank you for your input so far! >> >> The RFC is 0.2 with the last updates based on your feedback - I would >> like to add a suitable case example for annotations that require >> context before I'd say this RFC is 1.0. >> >> If anyone can point at a real case example using Doctrine Annotations >> (or anything else) please do! >> >> Thanks, >> Rasmus >> >> >> On Sat, May 14, 2016 at 4:38 AM, Benjamin Eberlei >> wrote: >> > >> > >> > On Fri, May 13, 2016 at 2:11 PM, Rasmus Schultz >> > wrote: >> >> >> >> Dear Internals, >> >> >> >> I'm announcing a simplified RFC for annotations: >> >> >> >> https://wiki.php.net/rfc/simple-annotations >> >> >> >> It's an alternative to the proposed Attributes RFC and the (2010) >> >> Annotations RFC. >> >> >> >> I'm attempting with this to describe a feature that is closer to the >> >> language than what is proposed by the Attributes RFC, by permitting >> >> the use of any valid PHP expression as an annotation value. >> >> >> >> Where the Attributes RFC proposes new syntax for what is essentially >> >> arrays, this proposal instead permits you to use actual arrays, e.g. >> >> without inventing any new syntax. It also allows you to use values of >> >> any other type, including objects. This makes the proposed feature >> >> more immediately useful, with a minimal learning curve. >> > >> > >> > The attributes RFC actually does not specific syntax or arrays, but >> > either a >> > scalar (number, string) or an ast\node. >> > >> > A proposal for arrays *would* actually be "closer to the language" as >> > you >> > describe it
Re: [PHP-DEV] [RFC] Simple Annotations
On 14/05/2016 20:49, Rasmus Schultz wrote: Dan, I've added a note about special annotations to the "future scope" section, naming the memoization-annotation as an example. This doesn't really explain how such a feature would fit it into the proposal. For instance, how can we avoid the syntax clashing with existing userland issues? Given that annotations in this proposal don't have names, we can't just reserve all names beginning "__" or "php\". Perhaps they would be pseudo-function calls, like <<__memoize()>>? How would that (or any other undefined function) act if accessed on a version that didn't have the feature? I realise you don't want to define the details, but an idea of what an annotation targeting the engine would look like would make the future-proofing clearer. Regards, -- Rowan Collins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] Simple Annotations
On 5/13/2016 2:11 PM, Rasmus Schultz wrote: > Dear Internals, > > I'm announcing a simplified RFC for annotations: > > https://wiki.php.net/rfc/simple-annotations > -1 again, I am sorry. The problem is that an annotation by definition is just meta-data. Your proposal however might result in fatal errors from meta-data: $reflector = new ReflectionClass(User::class); $reflector->getAnnotations(); // Fatal error: Uncaught Error: Class 'TableName' not found ... This is an absolute No-Go for meta-data. -- Richard "Fleshgrinder" Fussenegger signature.asc Description: OpenPGP digital signature
Re: [PHP-DEV] [RFC] Simple Annotations
On 5/13/2016 2:57 PM, Davey Shafik wrote: > 2) I like the idea of Hacks memoize for example, how do you see those being > implemented? Or don't you? > I wrote it in the /attribute grammar/ thread and I write it here again. Stuff like memoize should be implemented as keywords as part of the language and not via annotations. memoized function f() {} Same goes for deprecated: deprecated memoized function f() {} Or other special effects: deprecated inlined memoized function f() {} These things are not meta-data for userland, they are meta-data for PHP and should be part of, well, PHP. -- Richard "Fleshgrinder" Fussenegger signature.asc Description: OpenPGP digital signature
Re: [PHP-DEV] [RFC] Simple Annotations
Rowan, I'm afraid I don't follow. Why is it a problem to simply add a function or class to the global namespace? << new Memoize() >> or maybe a class with factory functions for built-in annotations: << Meta::memoize() >> I tend to agree with Richard though, that system directives really ought to be supported by the language with keywords rather than annotations. Richard, Regarding your point of view: > The problem is that an annotation by definition is just meta-data. Your > proposal however might result in fatal errors from meta-data I agree that annotations are just meta-data. Values. I disagree that we can't permit the use of PHP expressions to generate those values. You can't eliminate problems such as missing classes - and you shouldn't. With the examples in the Attributes RFC, as well as with e.g. Doctrine Annotations, we have the exact same problems, they're just one step removed from the language feature, which only adds complexity and makes it harder to debug these things. The only work-around to an issue such as a missing class, is to complete ignore the annotation - which leads to undefined behavior and scenarios that are even harder to debug. The other big problem with this position, is that it goes more or less directly against what the community wants - the majority of those who use annotations in PHP today, use Doctrine Annotations, and they expect annotations as classes. Source code can contain errors. I don't think there's really any way around that. Even if you wittled down metadata to the absolute minimum - a string, such as what it is in Go code, that string is invariably going to contain something that needs to be parsed or interpreted somehow, somewhere, at some point, and it can lead to errors. In my opinion, it's better to allow those errors to be caught early - for example, being able to catch syntax errors in expressions already at load-time. The attributes RFC is much worse in this regard - you could be lugging around all kinds of dependency errors, missing classes and whatnot, and you'd never know about it until the day you hit one of those annotations. Now instead, you're just stuck having to type out redundant unit-tests to prove that every annotation is valid, when the language could have taken care of that in a much simpler way. The other issue is dependencies. The Attributes RFC will lead to code with all sorts of hidden dependencies - possibly dependencies that you can't even know about, because you can't know how your metadata is going to be interpreted precisely. You can't know which classes your metadata might get mapped to or how. Simple Annotations take care of that, and they force you to e.g. `composer require` the package that provides the annotation classes you're using - this is a good thing. Hiding dependencies makes it really difficult for others to reason about your code. The Attributes RFC will lead to a lot of hidden rules - whereas Simple Annotations are simple and straight-forward, nothing hidden or implicit, your dependencies visible and traceable, which will lead to much better tooling. For example, an IDE or offline code analysis tool would catch syntax errors and missing classes and warn about those right away. I'm sorry, but I think that the idea of meta-data that can't error somehow, somewhere, at some point, is completely unrealistic. And I'd much rather have a simple facility that enables those errors to surface quickly. On Mon, May 16, 2016 at 11:16 AM, Rowan Collins wrote: > On 14/05/2016 20:49, Rasmus Schultz wrote: >> >> Dan, >> >> I've added a note about special annotations to the "future scope" >> section, naming the memoization-annotation as an example. > > > This doesn't really explain how such a feature would fit it into the > proposal. For instance, how can we avoid the syntax clashing with existing > userland issues? Given that annotations in this proposal don't have names, > we can't just reserve all names beginning "__" or "php\". > > Perhaps they would be pseudo-function calls, like <<__memoize()>>? How would > that (or any other undefined function) act if accessed on a version that > didn't have the feature? > > I realise you don't want to define the details, but an idea of what an > annotation targeting the engine would look like would make the > future-proofing clearer. > > Regards, > -- > Rowan Collins > [IMSoP] > > > > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] Simple Annotations
On 16/05/2016 14:05, Rasmus Schultz wrote: > I'm afraid I don't follow. Why is it a problem to simply add a > function or class to the global namespace? > > << new Memoize() >> > > or maybe a class with factory functions for built-in annotations: > > << Meta::memoize() >> Well, those particular names look quite likely to collide with users' code, though I guess more obviously reserved names could be used. More generally, though, neither feels a natural fit, because of the need to specify a value, rather than a name. On Reflection, what would an instance of class Memoize look like? Could you call "new Memoize" somewhere else? If a memoize() function was executed, what value would it return? Since you can only see the evaluated value, how could you detect the annotation for debugging? Indeed, if we're interested only in values, not where they come from, are the following all equivalent, and the mechanics totally unndetectable by Reflection? <> <<(function(){return null;})()>> function foo() { return null; } << foo() >> If the function form doesn't receive any context, its only purpose would be to copy some runtime global state into the annotation value on first access, which seems like a strange thing to do. > I tend to agree with Richard though, that system directives really > ought to be supported by the language with keywords rather than > annotations. That would be one way around it, indeed, so rather than "Future Scope", you could label "compiler directive" type annotations as "deliberately out of scope". Regards, -- Rowan Collins [IMSoP]
Re: [PHP-DEV] [RFC] Simple Annotations
On 5/16/2016 3:05 PM, Rasmus Schultz wrote: > I'm sorry, but I think that the idea of meta-data that can't error > somehow, somewhere, at some point, is completely unrealistic. > > And I'd much rather have a simple facility that enables those errors > to surface quickly. > It should error and it should error exactly at the point when you want to use it and at no other point. Right now they will error anytime anyone wants to do anything with any annotation. I fully understand the urge to directly access objects and I actually support it and I want them to error out at the point where you try to instantiate them. However, not when I simply access annotations in introspection. According to your logic I have to load the whole dependency chain, even thought I just want to generate my documentation that might have some annotations in use. I also need to load all my dependencies even though I wanted to leave the security dependency out because I wanted to easily disable the security annotation system locally for development. I even have to load the whole dependency chain, even though I just want to introspect the single data structure at hand without invoking anything. Even worse if I am using a package that has Doctrine annotations but I use it without them: /fatal error/ Nice? No! Solutions? Make the /simple/ annotations /simple/. ;-) Only allow scalar data to be defined and allow userland to register themselves for specific annotations. I mentioned this before but somehow everyone wants something more fancy and I have no karma (even though requested but ignored) to write my own RFC. That being said, you have the ability to write an RFC so please just take it. :-) Some PHP code to illustrate what I was thinking of since I am better with code than with words. :-P https://gist.github.com/Fleshgrinder/d26cd4751827b8c10a0dc33da32b48c3 Reflection is the wrong tool for the job to build annotation systems. Reflection should not be used in production it should be used to introspect data structures and reason about them or other stuff during unit tests. However, reflection should provide the ability to read annotations, it just does not do anything with them by default and outputs stupid arrays. You will definitely understand what I mean it you follow the Gist. I am sure there is room for improvement and that I overlooked something in my Gist. But I think it is a starting point, some additional notes: - I do not see any benefit in annotations for parameters. - I do not see any benefit in annotations for Generators. - I do not see any benefit for annotations in php-src. My proposal would include to ban annotations from php-src and make it a pure userland functionality. This has many advantages: - No name collisions. - Clear policy for the future. - php-src features are never optional. - php-src does not provide meta-data. Let me know what you think or if you have questions in regards to the Gist that are unclear. (Or maybe you found mistakes.) -- Richard "Fleshgrinder" Fussenegger signature.asc Description: OpenPGP digital signature
Re: [PHP-DEV] [RFC] Simple Annotations
Richard, I believe I have already stated my position on all of these concerns in my last email. Short recap: There's no such thing as optional dependencies - if you depend on a Doctrine ORM annotation, you depend on that package. The code should (must) error the moment you try to instantiate those annotations - whether you intend to use them or not is besides the question - a missing class is an error, in annotations as it is anywhere else in your code. Silently ignoring missing annotations solves nothing - it just leads to errors that are even harder to find and debug. You seem awfully concerned about name collisions? Most packages use a vendor\package namespace convention now - PHP has always been adding things to the global namespace, and it's never been a really substantial problem. I'm not that concerned. The one new thing you brought up here, is the issue of potentially loading/instantiating a bunch of annotations that go unused. From a dependency point of view, I maintain that that's correct and it should error out on missing classes. >From a performance point of view, you may have a valid concern, and this had occurred to me before - but I decided to leave it alone, for a couple of reasons. The most important reason being, look at how annotations get used in practice: controllers get things like route and filter annotations, all of which are usually needed at the same time; entities get ORM annotations, all of which are usually needed at the same time; form models get validation and input-type annotations, all of which are usually needed at the same time. And so on. The fact of the matter is that classes tend to have a single responsibility, and this usually means that applicable annotations tend to belong to a specific domain, and are needed at the same time. In other words, this problem is pretty theoretical - in reality, it's a very small problem, which, if somebody was very concerned about, could absolutely be solved in userland, by adding a cache layer. Which projects like Doctrine Annotations would likely do anyway, in order to cache the result of mode complex annotation logic to control inheritance, cardinality, applicability, etc. - all the complex stuff that this RFC stays away from. The extreme case of what you're proposing, is what Go does - an annotation is simply a string. No chance of any error, anywhere, ever, right? Wrong. People put JSON data in those strings for example, and parsing that data leads to run-time errors instead. That's the extreme example, but the problem with what you're proposing is precisely the same: you allow something like nested array data structures, those are still going to get interpreted and mapped to objects at run-time, and it leads to run-time errors instead. I maintain that the majority use-case is object annotations - and that deferring the construction of those objects doesn't solve anything. You're just deferring or hiding the problem, adding complexity, and mental as well as practical debugging effort - but the problem doesn't go away. I am and will always be in favor of language design that takes the most direct route to satisfy a requirement. In this case, the requirement is annotations as objects - and the most natural and direct route is the "new" keyword, static method calls, or whatever way you normally create objects; inventing a more indirect way to do that just adds complexity. On Mon, May 16, 2016 at 8:39 PM, Fleshgrinder wrote: > On 5/16/2016 3:05 PM, Rasmus Schultz wrote: >> I'm sorry, but I think that the idea of meta-data that can't error >> somehow, somewhere, at some point, is completely unrealistic. >> >> And I'd much rather have a simple facility that enables those errors >> to surface quickly. >> > > It should error and it should error exactly at the point when you want > to use it and at no other point. Right now they will error anytime > anyone wants to do anything with any annotation. > > I fully understand the urge to directly access objects and I actually > support it and I want them to error out at the point where you try to > instantiate them. However, not when I simply access annotations in > introspection. > > According to your logic I have to load the whole dependency chain, even > thought I just want to generate my documentation that might have some > annotations in use. I also need to load all my dependencies even though > I wanted to leave the security dependency out because I wanted to easily > disable the security annotation system locally for development. I even > have to load the whole dependency chain, even though I just want to > introspect the single data structure at hand without invoking anything. > > Even worse if I am using a package that has Doctrine annotations but I > use it without them: /fatal error/ > > Nice? No! > > Solutions? > > Make the /simple/ annotations /simple/. ;-) > > Only allow scalar data to be defined and allow userland to register > themselves for specific annotations.
Re: [PHP-DEV] [RFC] Simple Annotations
I'd like to finish this RFC, but I have two remaining issues. The syntax is a minor issue - since any valid PHP expression can be used, the bit-shift ambiguity is technically an issue, however marginal. A lot of people commented on the syntax when I posted the RFC on reddit - they don't like it. Anyone have any ideas for an alternative syntax? It needs to be delimited, e.g. needs to use opening and closing delimiters or a recognizable opening delimiter... Here's some ideas: @{ new Table("user") } @[ new Table("user") ] {{ new Table("user") }} +{ new Table("user") } I don't like any of these really, but the bit-shift operator isn't going when the stuff inside is an expression which could include bit-shift (is it?) The other issue is the dependency (context) injection example - no one seems to be able to cite an actual use-case, and if that's the case, I should probably just remove it from the RFC entirely? @Larry can you think of a case example in the myriad annotations you've seen in Drupal code? :-) Anyone else using Doctrine Annotations actually seen anyone making use of a closure in an annotation? On Tue, May 17, 2016 at 11:06 AM, Rasmus Schultz wrote: > Richard, > > I believe I have already stated my position on all of these concerns > in my last email. > > Short recap: > > There's no such thing as optional dependencies - if you depend on a > Doctrine ORM annotation, you depend on that package. > > The code should (must) error the moment you try to instantiate those > annotations - whether you intend to use them or not is besides the > question - a missing class is an error, in annotations as it is > anywhere else in your code. > > Silently ignoring missing annotations solves nothing - it just leads > to errors that are even harder to find and debug. > > You seem awfully concerned about name collisions? Most packages use a > vendor\package namespace convention now - PHP has always been adding > things to the global namespace, and it's never been a really > substantial problem. I'm not that concerned. > > The one new thing you brought up here, is the issue of potentially > loading/instantiating a bunch of annotations that go unused. From a > dependency point of view, I maintain that that's correct and it should > error out on missing classes. > > From a performance point of view, you may have a valid concern, and > this had occurred to me before - but I decided to leave it alone, for > a couple of reasons. The most important reason being, look at how > annotations get used in practice: controllers get things like route > and filter annotations, all of which are usually needed at the same > time; entities get ORM annotations, all of which are usually needed at > the same time; form models get validation and input-type annotations, > all of which are usually needed at the same time. And so on. > > The fact of the matter is that classes tend to have a single > responsibility, and this usually means that applicable annotations > tend to belong to a specific domain, and are needed at the same time. > > In other words, this problem is pretty theoretical - in reality, it's > a very small problem, which, if somebody was very concerned about, > could absolutely be solved in userland, by adding a cache layer. Which > projects like Doctrine Annotations would likely do anyway, in order to > cache the result of mode complex annotation logic to control > inheritance, cardinality, applicability, etc. - all the complex stuff > that this RFC stays away from. > > The extreme case of what you're proposing, is what Go does - an > annotation is simply a string. No chance of any error, anywhere, ever, > right? Wrong. People put JSON data in those strings for example, and > parsing that data leads to run-time errors instead. That's the extreme > example, but the problem with what you're proposing is precisely the > same: you allow something like nested array data structures, those are > still going to get interpreted and mapped to objects at run-time, and > it leads to run-time errors instead. > > I maintain that the majority use-case is object annotations - and that > deferring the construction of those objects doesn't solve anything. > > You're just deferring or hiding the problem, adding complexity, and > mental as well as practical debugging effort - but the problem doesn't > go away. > > I am and will always be in favor of language design that takes the > most direct route to satisfy a requirement. In this case, the > requirement is annotations as objects - and the most natural and > direct route is the "new" keyword, static method calls, or whatever > way you normally create objects; inventing a more indirect way to do > that just adds complexity. > > > On Mon, May 16, 2016 at 8:39 PM, Fleshgrinder wrote: >> On 5/16/2016 3:05 PM, Rasmus Schultz wrote: >>> I'm sorry, but I think that the idea of meta-data that can't error >>> somehow, somewhere, at some point, is completely unrealistic. >>> >>> And I'd muc
Re: [PHP-DEV] [RFC] Simple Annotations
On 17/05/2016 15:22, Rasmus Schultz wrote: The other issue is the dependency (context) injection example - no one seems to be able to cite an actual use-case, and if that's the case, I should probably just remove it from the RFC entirely? @Larry can you think of a case example in the myriad annotations you've seen in Drupal code? :-) Anyone else using Doctrine Annotations actually seen anyone making use of a closure in an annotation? Possibly nobody's responded to your call for an example because they're not sure what it is you're asking for an example of. I don't really understand what closures have to do with annotations, or how that relates to capturing context. An example from the other thread of a context-bound annotation would be implementing validation like this: <> function foo(int $percentage) This would appear to be unsupported as such by your proposal, so you would instead just have to use strings to represent how it needed to be accessed: <> and then have extra machinery in the reflection logic to extract the parameters and pass them to the object, e.g. $annotation->validate($reflected_parameters) This might be the more sensible approach to such an annotation anyway, but without any access to variables, I don't really understand the value of evaluating an expression for the annotation, just to get a constant value that is cached by the Reflection infrastructure. If the expression would have to be made up entirely of constants anyway, might the same "constant expressions" used in class const definitions be a better fit than "any valid PHP expression" - plus a specific exception for creating objects out of those constant expressions. Regards, -- Rowan Collins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] Simple Annotations
On 05/17/2016 04:22 PM, Rasmus Schultz wrote: I'd like to finish this RFC, but I have two remaining issues. The syntax is a minor issue - since any valid PHP expression can be used, the bit-shift ambiguity is technically an issue, however marginal. A lot of people commented on the syntax when I posted the RFC on reddit - they don't like it. Anyone have any ideas for an alternative syntax? It needs to be delimited, e.g. needs to use opening and closing delimiters or a recognizable opening delimiter... Here's some ideas: @{ new Table("user") } @[ new Table("user") ] {{ new Table("user") }} +{ new Table("user") } I don't like any of these really, but the bit-shift operator isn't going when the stuff inside is an expression which could include bit-shift (is it?) The other issue is the dependency (context) injection example - no one seems to be able to cite an actual use-case, and if that's the case, I should probably just remove it from the RFC entirely? @Larry can you think of a case example in the myriad annotations you've seen in Drupal code? :-) That's on my todo list for later this week. :-) I'm recently arrived in Paris so jetlagged beyond coherent thought at the moment, but I will see what I can do on a Drupal case study. --Larry Garfield -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] Simple Annotations
@Rasmus: This approach is too broad, allowing situations like Marco pointed out. I'd have to vote -1 on it too if you move forward, specially if you consider things like << "How do I grab this?" >> and other weirdness. @Rowan: Annotations should be immutable by nature. Still, I would love if you can detail (in the context of your example) what should be the output in the following examples: <> function foo(int $percentage) { $reflection = new \ReflectionFunction(__FUNCTION__); var_dump($reflection->getAnnotations()); // #2 } $reflection = new \ReflectionFunction("foo"); var_dump($reflection->getAnnotations()); // #1 foo(10); When executing the code, what should I expect from #1 and #2? I can foresee that #1 should crash (?) or should it return an ast\node or what? It all sounds reasonable on #2 what everyone is heavily forcing/suggesting because of DbC, but it *only* makes sense in the context of a function call (or a bound object in case of OO members). In any other case its outcome is unpredictable. There're 2 potential approaches I can foresee with future for Annotations: 1- This one I see as a simple approach. Take the idea of Dmitry and extend to support scalars and arrays (only, no objects, no AST\Nodes). Example: << Entity(["name" => "User"]) >> Strings could be easily converted into AST\Nodes internally and DbC would be possible. Context is inferred at libraries' desire. Solution here would not have a 1-1 dependency with classes, simplifying the implementation for meta parameters such as "override", "inherit", "memoize", "jit", etc. 2- Take an OO approach and correlate 1-1 class. But before that I'd tackle object initialization first. We'd require to make this to work: new Foo() { "property" => "value" } After that, we could get back to Annotations and support a subset of PHP grammar, similar to this: << Foo { "property" => "value" } >> Why I suggest this approach, some may ask? Simple young padawans... We're already seeing people discuss about final/read-only properties, where they could only be set at constructor time. We could easily expose this possibility through scopes. The {} block would be post constructor, but still in the initialization context, allowing sets to happen for read-only properties. It's a smart solution against adding a long list of constructor arguments (and handling default values for each one) and also a nice alternative to "named parameters". However, this approach would still require a 1-1 dependency with classes, which would make "override", "inherit", "jit" a PITA, and likely empower a more robust annotation solution (like defining which class can be an annotation and what cannot, or specify annotation target, like class, method, property, etc). PS: I hate to write long emails... =( Cheers, On Tue, May 17, 2016 at 11:28 AM, Rowan Collins wrote: > On 17/05/2016 15:22, Rasmus Schultz wrote: > >> The other issue is the dependency (context) injection example - no one >> seems to be able to cite an actual use-case, and if that's the case, I >> should probably just remove it from the RFC entirely? >> >> @Larry can you think of a case example in the myriad annotations >> you've seen in Drupal code? :-) >> >> Anyone else using Doctrine Annotations actually seen anyone making use >> of a closure in an annotation? >> > > Possibly nobody's responded to your call for an example because they're > not sure what it is you're asking for an example of. I don't really > understand what closures have to do with annotations, or how that relates > to capturing context. > > An example from the other thread of a context-bound annotation would be > implementing validation like this: > > <> > function foo(int $percentage) > > > This would appear to be unsupported as such by your proposal, so you would > instead just have to use strings to represent how it needed to be accessed: > > <> > > and then have extra machinery in the reflection logic to extract the > parameters and pass them to the object, e.g. > $annotation->validate($reflected_parameters) > > > This might be the more sensible approach to such an annotation anyway, but > without any access to variables, I don't really understand the value of > evaluating an expression for the annotation, just to get a constant value > that is cached by the Reflection infrastructure. > > If the expression would have to be made up entirely of constants anyway, > might the same "constant expressions" used in class const definitions be a > better fit than "any valid PHP expression" - plus a specific exception for > creating objects out of those constant expressions. > > Regards, > -- > Rowan Collins > [IMSoP] > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > > -- Guilherme Blanco Lead Architect at E-Block
Re: [PHP-DEV] [RFC] Simple Annotations
On 17/05/2016 17:21, guilhermebla...@gmail.com wrote: It all sounds reasonable on #2 what everyone is heavily forcing/suggesting because of DbC, but it *only* makes sense in the context of a function call (or a bound object in case of OO members). In any other case its outcome is unpredictable. Yeah, you're right, access to an actual parameter value or runtime instance requires logic somewhere else to provide the context, and to run the check on every execution of a function requires some magic to inject it into the call stack, or the function body, so my example doesn't really make any sense. Regards, -- Rowan Collins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] Simple Annotations
On 5/17/2016 11:06 AM, Rasmus Schultz wrote: > I believe I have already stated my position on all of these concerns > in my last email. > Sorry if I cannot keep everything in my mind. On 5/17/2016 11:06 AM, Rasmus Schultz wrote: > There's no such thing as optional dependencies - if you depend on a > Doctrine ORM annotation, you depend on that package. > Both statements are not true. #1 A good example of an optional dependency can be found in the Symfony console package: https://github.com/symfony/console/blob/master/composer.json#L28 https://github.com/symfony/console/blob/master/Application.php#L814-L816 #2 I could have code in a third-party package that provides some default annotations which suggests me to install some annotation systems that are capable of handling exactly those annotations. However, I decide to only use one of the suggested annotation systems in my project. With your solution this is not possible because it always ends up in fatal errors; although I consciously decided to not install one suggested dependency. See #1 for such an example. The only solution for the maintainer of the third-party package would be to provide multiple versions of the same package: 1. No annotations. 2. Annotations from System X. 3. Annotations from System Y. 4. Annotations from System X and Y. 5. ... This is not an acceptable approach. On 5/17/2016 11:06 AM, Rasmus Schultz wrote: > The code should (must) error the moment you try to instantiate those > annotations - whether you intend to use them or not is besides the > question - a missing class is an error, in annotations as it is > anywhere else in your code. > It is not (again see #1) a problem at compile time and always at runtime. You can try this easily on your console: $ php -r 'if (false) { new NonExistingClass; }' $ php -r 'if (true) { new NonExistingClass; }' Fatal error: Uncaught Error: Class 'NonExistingClass' not found in Command line code:1 Stack trace: #0 {main} thrown in Command line code on line 1 On 5/17/2016 11:06 AM, Rasmus Schultz wrote: > Silently ignoring missing annotations solves nothing - it just leads > to errors that are even harder to find and debug. > My proposal errors as soon as you try to retrieve an annotation with the annotation reader which was not previously registered as such. However, you can use Reflection and introspect any existing annotation without any kind of error. That is how it should be! On 5/17/2016 11:06 AM, Rasmus Schultz wrote: > You seem awfully concerned about name collisions? > No, I am not. It is just a nice side product of my proposal. On 5/17/2016 11:06 AM, Rasmus Schultz wrote: > The one new thing you brought up here, is the issue of potentially > loading/instantiating a bunch of annotations that go unused. [...] > From a performance point of view [...] > The problem is parsing and interpreting them although they might not be used because the current path of logic will not go through them at all. This is not a micro optimization thing. You just don't want to interact with annotations at all until they are actually needed. This is true for any kind of PHP code. See console commands above. On 5/17/2016 11:06 AM, Rasmus Schultz wrote: > The extreme case of what you're proposing, is what Go does - an > annotation is simply a string. No chance of any error, anywhere, ever, > right? Wrong. People put JSON data in those strings for example, and > parsing that data leads to run-time errors instead. That's the extreme > example, but the problem with what you're proposing is precisely the > same: you allow something like nested array data structures, those are > still going to get interpreted and mapped to objects at run-time, and > it leads to run-time errors instead. > Runtime errors for a runtime feature, well, yes. Again, see console command example above. @someAnnotation('{"json": "data"}') function f(); @someAnnotation('invalid json') function x(); $reflector = new ReflectionFunction('f'); $annotations = $reflector->getAnnotations(); /* $annotations = [ 'someAnnotation' => [ ['{"json": "data"}'], ], ]; */ $reflector = new ReflectionFunction('x'); $annotations = $reflector->getAnnotations(); /* $annotations = [ 'someAnnotation' => [ ['invalid json'], ], ]; */ annotation_register('someAnnotation', function (string $json) { $decoded_json = json_decode($json); if ($decoded_json === false) { throw new JsonParseException(json_last_error_msg()); } return $decoded_json; }); $annotations = new AnnotatedFunction('f'); $some_annotations = $annotations->getAnnotations('someAnnotations'); echo $some_annotations[0]->json; // data $annotations = new AnnotatedFunction('x'); $some_annotations = $annotations->getAnnotations('someAnnotations'); // Fatal error: Uncaught JsonParseException in ... The above is just fine. The annotation library registered the callback and it knows w
Re: [PHP-DEV] [RFC] Simple Annotations
> I don't really understand what closures have to do with annotations, or how > that relates to capturing context. The point is that, rather than trying to capture context, you can just as well ask for what you need. I'm going to use a very contrived example, because I really can't think of any real case that requires context: class User { << new Validation(function ($value) { return false !== filter_var($value, FILTER_VALIDATE_EMAIL); }) >> public $email; } Like I said, extremely contrived - in practice, you wouldn't need to attach functionality in that way. > An example from the other thread of a context-bound annotation would be > implementing validation like this You're not annotating the function - this is just a round-about way of annotating the argument. You would do that directly, like this: function foo( << ValidateRange(0, 100) >> int $percentage ) { ... } > If the expression would have to be made up entirely of constants anyway, > might the same "constant expressions" used in class const definitions be a > better fit than "any valid PHP expression" - plus a specific exception for > creating objects out of those constant expressions. Probably not - what happens with what is today "nested annotations" then? Or will you make an exception for those too? The problem is, you're just reinventing a subset of the programming language, and I'm sure you can keep expanding on that indefinitely. What for? Just use the facilities already defined by the language. This fear of any feature that lets you do "evil" is incomprehensible to me. Most features of almost any programming language can be used for "evil". IMO, the real question is whether a feature accomplishes what you want. If you insist on something that also prevents things you don't want, you're bound to end up with something a lot more complex that fits into the language a lot less naturally... On Tue, May 17, 2016 at 5:28 PM, Rowan Collins wrote: > On 17/05/2016 15:22, Rasmus Schultz wrote: >> >> The other issue is the dependency (context) injection example - no one >> seems to be able to cite an actual use-case, and if that's the case, I >> should probably just remove it from the RFC entirely? >> >> @Larry can you think of a case example in the myriad annotations >> you've seen in Drupal code? :-) >> >> Anyone else using Doctrine Annotations actually seen anyone making use >> of a closure in an annotation? > > > Possibly nobody's responded to your call for an example because they're not > sure what it is you're asking for an example of. I don't really understand > what closures have to do with annotations, or how that relates to capturing > context. > > An example from the other thread of a context-bound annotation would be > implementing validation like this: > > <> > function foo(int $percentage) > > > This would appear to be unsupported as such by your proposal, so you would > instead just have to use strings to represent how it needed to be accessed: > > <> > > and then have extra machinery in the reflection logic to extract the > parameters and pass them to the object, e.g. > $annotation->validate($reflected_parameters) > > > This might be the more sensible approach to such an annotation anyway, but > without any access to variables, I don't really understand the value of > evaluating an expression for the annotation, just to get a constant value > that is cached by the Reflection infrastructure. > > If the expression would have to be made up entirely of constants anyway, > might the same "constant expressions" used in class const definitions be a > better fit than "any valid PHP expression" - plus a specific exception for > creating objects out of those constant expressions. > > > Regards, > -- > Rowan Collins > [IMSoP] > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] Simple Annotations
On 5/17/2016 7:47 PM, Rasmus Schultz wrote: > You're not annotating the function - this is just a round-about way of > annotating the argument. > > You would do that directly, like this: > > function foo( > << ValidateRange(0, 100) >> > int $percentage > ) { ... } > What is this good for? When is /ValidateRange/ being evaluated? On every call? How? I really do not get it, sorry. -- Richard "Fleshgrinder" Fussenegger signature.asc Description: OpenPGP digital signature
Re: [PHP-DEV] [RFC] Simple Annotations
On 17/05/2016 18:47, Rasmus Schultz wrote: I don't really understand what closures have to do with annotations, or how that relates to capturing context. The point is that, rather than trying to capture context, you can just as well ask for what you need. I'm going to use a very contrived example, because I really can't think of any real case that requires context: class User { << new Validation(function ($value) { return false !== filter_var($value, FILTER_VALIDATE_EMAIL); }) >> public $email; } Like I said, extremely contrived - in practice, you wouldn't need to attach functionality in that way. Right, in my mind this is less about *context*, and more about *passing code into the implementation*. I think we agree that an implementation of validation-by-annotation would look something like: $validators = $reflected_thing->getAnnotations('someFilter'); foreach ( $validators as $validator ) { $validator->validate($some_instance); } This gets its *context* from the code running the validator; the implementation of the validate() method would normally just be part of the annotation type: <> var $foo; The closure example you gave would be a way of providing on-the-fly dynamic *behaviour* as the argument to an annotation. This wouldn't really make sense for validation (a custom validator would still probably be defined once and used for several fields, but theoretically you could have: <> It's not the context that's magic here, it's the ability to specify code as part of the annotation *value*, rather than the annotation *definition*. A more obvious example therefore is using annotations to implement DbC: <> Although as I admitted to Guilherme, that requires some magic to run it at the right time anyway... An example from the other thread of a context-bound annotation would be implementing validation like this You're not annotating the function - this is just a round-about way of annotating the argument. You would do that directly, like this: function foo( << ValidateRange(0, 100) >> int $percentage ) { ... } Sure, but it's easy to come up with a variant that requires access to more than one parameter, say: << ValidateLessThan($min, $max) >> function foo($min, $max) Which without any context or AST would need to be either: << ValidateLessThan('$min', '$max') >> function foo($min, $max) ...which rather defeats the point of having code rather than just a string. Or: << ValidateCustom(function($min, $max) { return $min < $max } >> function foo($min, $max) ...which feels rather clunky. If the expression would have to be made up entirely of constants anyway, might the same "constant expressions" used in class const definitions be a better fit than "any valid PHP expression" - plus a specific exception for creating objects out of those constant expressions. Probably not - what happens with what is today "nested annotations" then? Or will you make an exception for those too? How would you "nest" annotations in your scheme anyway? As far as I can see, they would be either multi-level arrays (a valid constant expression) or nested constructor calls. So there's still exactly one exception made, the "new Foo(args)" construct. The problem is, you're just reinventing a subset of the programming language, and I'm sure you can keep expanding on that indefinitely. What for? Just use the facilities already defined by the language. That's a fair point. The flipside is that the more things you allow the user to do, the more edge cases you have to deal with when evaluating the expressions in your special "zero context". This fear of any feature that lets you do "evil" is incomprehensible to me. Most features of almost any programming language can be used for "evil". I am not suggesting removing the ability to avoid people doing evil; I'm suggesting that the feature you call "simple annotations" could be a lot simpler. What is the value of being able to write this: function foo() { return 'MyAnnotation'; } << foo() >> class A {} When you could just write this: << 'MyAnnotation' >> IMO, the real question is whether a feature accomplishes what you want. If you insist on something that also prevents things you don't want, you're bound to end up with something a lot more complex that fits into the language a lot less naturally... In my mind, something that lets me evaluate an arbitrary expression in some weird null-context, at an unspecified time (because I don't know what code will be the first to request that annotation), only to have the result stored in some invisible static variable, is *more* complex than many of the alternatives. Regards, -- Rowan Collins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] Simple Annotations
So this thread kind of died, apparently mostly due to some concerns about possible side-effects. I would just like to point out one thing: when it comes to metadata/annotations, whatever you come up with, even if it's something that allows only constant expressions and simple data-structures, as opposed to classes, what's going to happen - like, immediately - is someone will figure out how to map it to classes. The thing is that, even if annotations are simple data attached to source-code elements, someone is going to need/want things like range and type validations for those annotations and their attributes - essentially the only way to get that, is with code, and the most obvious way to do that, is map annotations to classes, which is basically what every userland annotation project is doing. I maintain the position that simply allowing arbitrary expressions as annotations is the simplest, most direct way to this feature. Whatever you come up with that is more restrictive, someone will simply figure out how to map that to the thing they really wanted, which is objects - and then you have all the same problems and risks of global/mutable state, side-effects, etc. Anything directly or indirectly executable is going to allow you to create those problems - presently that is true everywhere in PHP, literally for everything, including files, classes, functions, even expressions can have side-effects. Inventing a more restrictive form of annotations will only make the road longer and more confusing - it won't remove those risks or solve those problems. The only real solution to those problems, is teach people how to program. Another concern was about optional dependency on classes for annotations. Again, that's going to happen when people figure out how to map a more restrictive feature to classes - and in either case, if you could somehow ignore annotations that fail to initialize because of a missing dependency, that would be a very bad idea; quietly ignoring a missing class would create very strange and difficult debugging situations. I think that this problem is exaggerated. Needing to include the annotations you actually use is hardly a roadblock to anything. Leaving out dependencies would be as strange to me as allowing the extends or implements keyword to ignore a missing interface or base class. I simply don't see the usefulness, and can only see that turning into a real debugging nightmare. I think that, given how simple this proposed feature really is, it's really not likely to generate a lot of surprises for anyone - I mean, we're used to the fact that new Foo() fails if the class Foo does not exist. In fact, it would be a lot more surprising if you had to explain why that wasn't the case. We're also used to the fact that such expressions fail not when they're loaded, but when they're evaluated - like when you run a method. That's not surprising at all. I think that this feature is really very simple, in every sense of the word - it's easy to explain, there are very few rules and no real surprises, it just basically lets you insert a list of values or objects defined using simple everyday PHP expressions, which behave, for better or for worse, like they do every day. I wish you wouldn't shoot down this feature on the basis of some missing language qualities you seem to be seeking - if PHP someday supports immutable objects, or functions without side-effects, annotations would immediately support those too. At the moment, it does not, and I don't believe it's going to be even the least bit surprising that annotations don't have those language qualities either. Nor do I think people are going to do "evil" with it - I think that for example Symfony annotations, or annotations in C#, have all of the same problems, just like any language with global and mutable state, and people are quite happy and productive using them anyway. I think that there is real value to having a very simple feature that leverages the language as-is instead of inventing new, special syntax, restrictions or limitations, and I'd be much less worried about "evil" somebody might do with that, and more excited to see the interesting and clever things people would come up with. It seemed like there was some initial excitement about this idea. I'm not opposed to developing the idea further, if there's any interest. For example, one possible idea would be to allow object constructions only: class User { << Length(20) >> public $email; } This creates a limitation, but covers the most popular use-case of object annotations, and that limitation would enable a reflection reader along the lines of: $annotations = $reflection->getProperty("email")->getAnnotations(User::class); This differs from what I previously proposed, since you are now required to ask specifically for one type of annotation, which means you can have "optional" annotations - just check first, e.g. if (class_exists(Length::class
Re: [PHP-DEV] [RFC] Simple Annotations
Tonight I started trying to write a proposal for even more simplified annotations, e.g. something simple enough that it cannot result in fatal errors. I eventually gave up, and here's why. Essentially, if you have to restrict yourself to things that cannot under any circumstances error out, the only things you can use are constant scalar values - even constant scalar expressions could not be allowed, because even those could error when CONST_NAME or Class::CONST_NAME aren't defined. So, in a nutshell, what you're saying (Richard) is that annotations can only consist of scalar values, and possibly arrays of values? (and I don't even see your gist doing that - it's just invoking closures, as far as I can tell, and any of those could error, or not?) So, regardless of syntax, literally the only annotation data allowed would be blatantly simple things like: ["max_length" => 20] ["data_type" => "int"] Or in other words, constant arrays, int, string, float and bool values. Nothing else. Nothing can depend on any kind of external symbol (class names, constants, etc.) since any such reference can cause an error. No static analysis or type-checking of any kind can ever be performed - no assertions or declarations can be made about the shape or types of metadata, at all. The problem is, that's simply not useful. It's also not very different from what we have today with php-doc blocks - the only real difference is we can check array nesting and int, string, float and bool syntax, e.g. nothing really useful or important. In other words, this would be a very slight improvement on syntax - nothing that actually amounts to any kind of functionality. If we were to ship a feature like that, what would happen is precisely the same thing that happened with php-doc blocks: the real work will be done in userland (and likely dominated by one library), there will be no IDE support (except proprietary support for that one dominating library), no useful offline static analysis tools. At the end of the day, what we end up with will have all of the same issues we have with php-doc blocks and existing libraries. I don't understand how such a feature would be useful to anybody? I especially don't understand why you want a language feature that isn't allowed to reference external symbols - e.g. isn't allowed to have any kind of relationship with anything declared in userland. It seems to me a peculiar, arbitrary and unrealistic restriction to put on annotations. It's going to have no immediate usefulness, and mapping the data to objects is going to happen in userland, just as it does today. I'd like to work on this proposal, but I feel that you're giving me nothing to work with here. On Mon, May 16, 2016 at 12:38 PM, Fleshgrinder wrote: > On 5/13/2016 2:11 PM, Rasmus Schultz wrote: >> Dear Internals, >> >> I'm announcing a simplified RFC for annotations: >> >> https://wiki.php.net/rfc/simple-annotations >> > > -1 again, I am sorry. > > The problem is that an annotation by definition is just meta-data. Your > proposal however might result in fatal errors from meta-data: > > $reflector = new ReflectionClass(User::class); > $reflector->getAnnotations(); > > // Fatal error: Uncaught Error: Class 'TableName' not found ... > > This is an absolute No-Go for meta-data. > > -- > Richard "Fleshgrinder" Fussenegger > -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] Simple Annotations
Hi Rasmus, On 27 June 2016 at 20:37, Rasmus Schultz wrote: > Tonight I started trying to write a proposal for even more simplified > annotations, e.g. something simple enough that it cannot result in > fatal errors. I eventually gave up, and here's why. > > Essentially, if you have to restrict yourself to things that cannot > under any circumstances error out, the only things you can use are > constant scalar values - even constant scalar expressions could not be > allowed, because even those could error when CONST_NAME or > Class::CONST_NAME aren't defined. > > So, in a nutshell, what you're saying (Richard) is that annotations > can only consist of scalar values, and possibly arrays of values? (and > I don't even see your gist doing that - it's just invoking closures, > as far as I can tell, and any of those could error, or not?) > > So, regardless of syntax, literally the only annotation data allowed > would be blatantly simple things like: > > ["max_length" => 20] > > ["data_type" => "int"] > > Or in other words, constant arrays, int, string, float and bool > values. Nothing else. > > Nothing can depend on any kind of external symbol (class names, > constants, etc.) since any such reference can cause an error. > > No static analysis or type-checking of any kind can ever be performed > - no assertions or declarations can be made about the shape or types > of metadata, at all. > > The problem is, that's simply not useful. > That's actually what doctrine/annotations has been for a while tho, and a lot of people rely just on that. The data-structure is still enforced by a VO-style class (the annotation itself) in the library, though. It would still be very useful, in my opinion. Note that the existing annotation libs out there do indeed trigger autoloading for referenced class constants. Marco Pivetta http://twitter.com/Ocramius http://ocramius.github.com/
Re: [PHP-DEV] [RFC] Simple Annotations
I completely agree with Marco. Throwing class-loading errors for value-object classes is alright. For logic-parsing inside annotations, I don't feel any need for it. Metadata should be static, IMO, but it could very well be (and should be) value-objects. >At the end of the day, what we end up with will have all of the same >issues we have with php-doc blocks and existing libraries. > >I don't understand how such a feature would be useful to anybody? As an addition, I don't think this feature should ADD new functionality to those already present on DocBlocks. I just feel like docblocks are an ugly hack for the lack of native annotations. Pasting from a previous discussion in this same list, Rowan and I were discussing whether to add a native docblock-annotation-retrieval feature or a native annotation syntax: 1) It might not be objectively bad to add this feature in docblocks, but it will be objectively wrong to keep calling them "DocBlocks" if they are no longer documentation-only blocks. 2) Even though PHP already treats docblocks differently from comments, that's not the common view on userland, nor is explained on the manuals. There are no separate entries to explain docblocks, and no mention to them on the "Comments" page. The Reflection method to retrieve DocBlocks is "getDocCOMMENT", which suggests they are comment that do not affect runtime behaviour. We'd have to update the comments page to say "'/*' starts a comment, unless if they're immediately followed by another asterisk ('/**'), in which case it may or may not be a comment, depends on the following token". It's very confusing. 3) To make this work within docblocks, we'd have to get rid of at least one configuration setting (opcode.save_comments). 4) Dockblocks are stripped from obfuscators and transpilers, which are a part of the ecosystem and do affect users, even if they are not first-class citizens here. Annotations in dockblocks are hard to understand because they they may or may not be runtime-affecting annotations and there is no way to tell. I hate this comparison with docblock annotations, because they are an UGLY HACK. It works wonderfully, yes, but so do global variables, singletons and what-not, and we are not using them anymore, are we? Adding this very same feature that already exists in userland as a dedicated syntax would fix a lot of issues, cited above. There is no need to evaluate logic inside annotations, let them be value-objects, let it throw errors autoloading said objects, and let's get this rolling. Please, don't embargo this ONCE again. It's a very much needed feature, and people want it! There have been innumerous discussions on reddit, and almost everyone agrees annotations are a missing feature and comment-annotations are an ugly hack for the lack of native syntax. Please, make this happen. 2016-06-28 11:12 GMT-03:00 Marco Pivetta : > Hi Rasmus, > > On 27 June 2016 at 20:37, Rasmus Schultz wrote: > > > Tonight I started trying to write a proposal for even more simplified > > annotations, e.g. something simple enough that it cannot result in > > fatal errors. I eventually gave up, and here's why. > > > > Essentially, if you have to restrict yourself to things that cannot > > under any circumstances error out, the only things you can use are > > constant scalar values - even constant scalar expressions could not be > > allowed, because even those could error when CONST_NAME or > > Class::CONST_NAME aren't defined. > > > > So, in a nutshell, what you're saying (Richard) is that annotations > > can only consist of scalar values, and possibly arrays of values? (and > > I don't even see your gist doing that - it's just invoking closures, > > as far as I can tell, and any of those could error, or not?) > > > > So, regardless of syntax, literally the only annotation data allowed > > would be blatantly simple things like: > > > > ["max_length" => 20] > > > > ["data_type" => "int"] > > > > Or in other words, constant arrays, int, string, float and bool > > values. Nothing else. > > > > Nothing can depend on any kind of external symbol (class names, > > constants, etc.) since any such reference can cause an error. > > > > No static analysis or type-checking of any kind can ever be performed > > - no assertions or declarations can be made about the shape or types > > of metadata, at all. > > > > The problem is, that's simply not useful. > > > > That's actually what doctrine/annotations has been for a while tho, and a > lot of people rely just on that. > The data-structure is still enforced by a VO-style class (the annotation > itself) in the library, though. > > It would still be very useful, in my opinion. > > Note that the existing annotation libs out there do indeed trigger > autoloading for referenced class constants. > > Marco Pivetta > > http://twitter.com/Ocramius > > http://ocramius.github.com/ >
Re: [PHP-DEV] [RFC] Simple Annotations
Note that I only make comparisons with doc-blocks to point out the fact that, functionally, they would not be very different from an annotation feature that solely permits you to annotate with values. A doc-block is a string, which is a value - an array/int/float/string/bool is a value. It's only very slightly better than a bare string, in the sense that neither doc-blocks nor simple values permit any kind of definition of which annotations are actually available, hence offer no means of validation of that data (except at run-time by a userland facility), provides no support for static analysis, no IDE support, no clear indication of real dependencies, and so on. Since the discussion isn't completely dead yet (thank you Pedro and Marco), I will just briefly explain what I had in mind for another RFC. This would be largely based on my previous proposal: https://wiki.php.net/rfc/simple-annotations But with the following differences. The syntax would no longer allow arbitrary expressions - instead, it would allow object annotations only, declared in one of three ways: << RequiresLogin >> // equivalent to new RequiresLogin() << Length(20) >> // equivalent to new Length(20) << HttpMethod::post() >> // equivalent to static method-call HttpMethod::post() The latter permits you to declare and call static factory methods, e.g. "named constructors" for some types of annotations. Annotations are now consistently objects, which simplifies filtering annotations, etc. This declaration syntax enables better in-language analysis - because the declarations always include the annotation class-name, this means it can now support deferred as well as conditional creation, by having a slightly different reflection API: $annotations = $class_reflection->getAnnotations(); At this point, we have a list of ReflectionAnnotation objects - the actual annotation objects have *not* been created at this point, which would allow for conditional evaluation, such as: foreach ($annotations as $annotation) { echo $annotation->className; // => "Length" etc. if ($annotation->classExists()) { var_dump($annotation->getInstance()); // => Length object, etc. } else { var_dump($annotation->getInstance()); // => UnknownAnnotation object } } The UnknownAnnotation object has three properties: class-name, method-name ("__construct" if constructor was used) and the list of arguments. In other words, this would provide support for "optional dependencies", in much the same way that unserialize() does it - the getInstance() method will trigger autoloading, and if the class is unavailable, it takes this "typeless" information and puts it in a general object, so that the information itself isn't lost. It may *seem* like this solves the optional dependency problem, but let me be clear, it does *not* fully address that issue, because something like << Method(Http::GET) >> could still fail, if the Method class is defined, but the Http class is not. That's a much more marginal case, but it's not completely unlikely that you would use constants from one dependency and pass them to annotation classes from another dependency, e.g. if the HTTP method constants were part of an HTTP package, and the annotation itself were part of a different package. Arguably it's much less likely to occur though - if you have the annotation package installed, most likely you have the dependent package of that package installed as well. So this is much safer, but just to be clear, the only way you can make it any safer than that, is by completely disallowing anything but values as arguments to annotation constructors - which, in my opinion, simply isn't useful. Alternatively to the above, you could imagine a simpler reflection facility that does eagerly construct annotations, but constructs UnknownAnnotation instances for missing classes - this would perhaps balance more towards immediate usefulness and less towards performance perfectionism, as it increases the chance of loading an unused annotation. This would be my preference though - as previously argued, the odds of having a large number of unrelated annotations on the same member are extremely low in practice; an annotated entity tends to be a persisted property, a form input element, or an action method, etc... rarely do you mix annotations from different domains onto the same member; annotations that are applicable to a given domain are usually relevant to a consumer belonging to the same (or a closely related) domain, so this performance concern is mostly a theoretical micro optimization. For another, eager construction means we can filter annotations with instanceof rather than by class-name, which is much powerful, enabling you to leverage inheritance - for example, you'd be able to ask directly for all instances of ValidationAnnotation and get those with a single call, which is incredibly useful. Anyways, let me know your feelings abou
Re: [PHP-DEV] [RFC] Simple Annotations
Hi Rasmus, I think you may be onto something here... :) On 28/06/2016 17:31, Rasmus Schultz wrote: The UnknownAnnotation object has three properties: class-name, method-name ("__construct" if constructor was used) and the list of arguments. I'm not sure we need an extra UnknownAnnotation class. It seems like all of that information could be available on the ReflectionAnnotation object, so you can get it without the class being available if you want to. If the idea is to lazily-evaluate the arguments to the annotation, that could be handled by storing the AST and only evaluating them when you call either $annotation->getInstance() or $annotation->getArguments(): use MyAnnotations\FooAnnotation as Foo; // Class alias resolved at compile time as normal << Foo(time()) >> class A {} $class_reflection = new ReflectionClass('A'); $annotations = $class_reflection->getAnnotations(); foreach ($annotations as $annotation) { if ( $annotation->getClassName() == 'MyAnnotations\\FooAnnotation' ) { var_dump( $annotation->getArguments() ); // `time()` evaluated here var_dump( $annotation->getInstance() ); // `new MyAnnotations\FooAnnotation(time())` evaluated here } } It's not the responsibility of the ReflectionAnnotation class to worry about calling the autoloader, issuing errors, etc, it should just delegate that to the engine like any other constructor call. There's no need for an extra $annotation->classExists() method, either, because you can just say "class_exists($annotation->getClassName())". Interestingly, this same delayed evaluation of arguments opens us up to include direct access to the AST as some (Nikita?) have asked for: // Constructor argument can be any valid expression, // however nonsensical it would be to evaluate it directly << Pre($a >= 42) >> function foo($a) {} $r = new ReflectionFunction('foo'); $annotations = $r->getAnnotations(); foreach ( $annotations as $annotation ) { if ( $annotation->getClassName() == 'Pre' ) { $ast = $annotation->getArgumentAST(); // Now we can do funky things with the AST, like evaluating it in a custom context :) } } Regards, -- Rowan Collins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] Simple Annotations
On 6/28/2016 6:31 PM, Rasmus Schultz wrote: > Since the discussion isn't completely dead yet (thank you Pedro and > Marco), I will just briefly explain what I had in mind for another > RFC. > I want to see this feature happen too but the right way, so let us just spend some more time wrapping our heads around it. We could also discuss and work on the RFC at a different place (GitHub with issues?), since many people are always complaining about too many emails that are sent to this ML. On 6/28/2016 6:31 PM, Rasmus Schultz wrote: > The syntax would no longer allow arbitrary expressions - instead, it > would allow object annotations only, declared in one of three ways: > > << RequiresLogin >> // equivalent to new RequiresLogin() > > << Length(20) >> // equivalent to new Length(20) > > << HttpMethod::post() >> // equivalent to static method-call > HttpMethod::post() > > The latter permits you to declare and call static factory methods, > e.g. "named constructors" for some types of annotations. > > Annotations are now consistently objects, which simplifies filtering > annotations, etc. > > This declaration syntax enables better in-language analysis - because > the declarations always include the annotation class-name, this means > it can now support deferred as well as conditional creation, by having > a slightly different reflection API: > > $annotations = $class_reflection->getAnnotations(); > > At this point, we have a list of ReflectionAnnotation objects - the > actual annotation objects have *not* been created at this point, which > would allow for conditional evaluation, such as: > > foreach ($annotations as $annotation) { > echo $annotation->className; // => "Length" etc. > > if ($annotation->classExists()) { > var_dump($annotation->getInstance()); // => Length object, etc. > } else { > var_dump($annotation->getInstance()); // => UnknownAnnotation > object >} > } > > The UnknownAnnotation object has three properties: class-name, > method-name ("__construct" if constructor was used) and the list of > arguments. > > In other words, this would provide support for "optional > dependencies", in much the same way that unserialize() does it - the > getInstance() method will trigger autoloading, and if the class is > unavailable, it takes this "typeless" information and puts it in a > general object, so that the information itself isn't lost. > Awesome! This is exactly the kind of thing that one wants from reflection, information and not exceptions or worse. :) On 6/28/2016 6:31 PM, Rasmus Schultz wrote: > It may *seem* like this solves the optional dependency problem, but > let me be clear, it does *not* fully address that issue, because > something like << Method(Http::GET) >> could still fail, if the Method > class is defined, but the Http class is not. That's a much more > marginal case, but it's not completely unlikely that you would use > constants from one dependency and pass them to annotation classes from > another dependency, e.g. if the HTTP method constants were part of an > HTTP package, and the annotation itself were part of a different > package. Arguably it's much less likely to occur though - if you have > the annotation package installed, most likely you have the dependent > package of that package installed as well. > > So this is much safer, but just to be clear, the only way you can make > it any safer than that, is by completely disallowing anything but > values as arguments to annotation constructors - which, in my opinion, > simply isn't useful. > I would argue that this should fail if the Method class from the example exists and is actually being created. However, if the Method class does not exist (and we construct an UnknownAnnotation stub for it) nothing should go wrong. I mean, the assumption that the dependency of a missing dependency is/might be missing sounds logical too me. Of course we would need to have something like an UnknownAnnotationArgument stub too. Note that this allows you in consequence to create other kinds of objects within annotations: << Annotation(new Argument) >> final class {} This would allow nesting as some DocBlock libraries allow it currently. On 6/28/2016 6:31 PM, Rasmus Schultz wrote: > Alternatively to the above, you could imagine a simpler reflection > facility that does eagerly construct annotations, but constructs > UnknownAnnotation instances for missing classes - this would perhaps > balance more towards immediate usefulness and less towards performance > perfectionism, as it increases the chance of loading an unused > annotation. > > This would be my preference though - as previously argued, the odds of > having a large number of unrelated annotations on the same member are > extremely low in practice; an annotated entity tends to be a persisted > property, a form input element, or an action method, etc... rarely do > you mix annotations from differen
Re: [PHP-DEV] [RFC] Simple Annotations
On 28/06/2016 17:31, Rasmus Schultz wrote: For another, eager construction means we can filter annotations with instanceof rather than by class-name, which is much powerful, enabling you to leverage inheritance - for example, you'd be able to ask directly for all instances of ValidationAnnotation and get those with a single call, which is incredibly useful. Sorry for the extra mail with only one point, but I missed this before. This can be achieved using the fully-qualified class-name by using is_a() or is_subclass_of(). This obviously needs to trigger the autoloader anyway, but can still avoid actually instantiating the class. (And thus, per my other mail, evaluating the args.) I think lazy construction (without magic stub classes) gives the best of both worlds here. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] Simple Annotations
Thanks, Richard and Rowan - this gives me more to work with! I will work on a new proposal and ask for more input when I have a draft ready :-) On Tue, Jun 28, 2016 at 7:56 PM, Rowan Collins wrote: > On 28/06/2016 17:31, Rasmus Schultz wrote: >> >> For another, eager construction means we can filter annotations with >> instanceof rather than by class-name, which is much powerful, enabling >> you to leverage inheritance - for example, you'd be able to ask >> directly for all instances of ValidationAnnotation and get those with >> a single call, which is incredibly useful. > > > Sorry for the extra mail with only one point, but I missed this before. This > can be achieved using the fully-qualified class-name by using is_a() or > is_subclass_of(). This obviously needs to trigger the autoloader anyway, but > can still avoid actually instantiating the class. (And thus, per my other > mail, evaluating the args.) > > I think lazy construction (without magic stub classes) gives the best of > both worlds here. > > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] Simple Annotations
> << HttpMethod::post() >> // equivalent to static method-call > HttpMethod::post() > What would this, specifically, do? Would it call HttpMethod::post() when the class gets instantiated and return its return on a ReflectionClass::getAnnotations()? Why is it necessary to be able to evaluate expressions on annotations? I do agree that throwing errors/exceptions when classes are not found is needed (otherwise we wont be able to use value-objects), but evaluating expressions seems a little crazy for me... why should metadata be computed in runtime?
Re: [PHP-DEV] [RFC] Simple Annotations
On 28 June 2016 20:10:15 GMT+01:00, Pedro Cordeiro wrote: >> << HttpMethod::post() >> // equivalent to static method-call >> HttpMethod::post() >> > >What would this, specifically, do? Would it call HttpMethod::post() >when >the class gets instantiated and return its return on a >ReflectionClass::getAnnotations()? Why is it necessary to be able to >evaluate expressions on annotations? I'm not sure if it's definitely necessary, but the stated use case was for factories / named constructors. PHP only has one constructor per class, so it's not uncommon to have static createFromX methods. Since this will eventually evaluate as "new Foo(42)": << Foo(42) >> The idea is that this would be available to call a factory instead: << Foo::fromRoman('XLII') >> The neat thing being that by limiting the options to this, you can still extract a class name for every annotation. I say "eventually", because as proposed, this won't actually happen until you explicitly ask for the "instance" of the annotation. See previous examples. Regards, -- Rowan Collins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] Simple Annotations
This was just an example of a value object - for example: class HttpMethod { private $method; protected function __construct($method) { $this->method = $method; } public function getMethod() { return $this->method; } public static function get() { return new self("GET"); } public static function post() { return new self("POST"); } // ... } You could picture using a flyweight constructor inside those factory methods maybe, and probably other patterns... I think there's plenty of cases for "named constructors", this is just the PHP variety of that. There are also cases (such as this one) where only certain constructions are permitted - allowing any string for HTTP method (e.g. wrong names, wrong case etc.) is prevented by protecting the constructor... On Tue, Jun 28, 2016 at 9:10 PM, Pedro Cordeiro wrote: > >> << HttpMethod::post() >> // equivalent to static method-call >> HttpMethod::post() > > > What would this, specifically, do? Would it call HttpMethod::post() when the > class gets instantiated and return its return on a > ReflectionClass::getAnnotations()? Why is it necessary to be able to > evaluate expressions on annotations? I do agree that throwing > errors/exceptions when classes are not found is needed (otherwise we wont be > able to use value-objects), but evaluating expressions seems a little crazy > for me... why should metadata be computed in runtime? -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] Simple Annotations
On 06/28/2016 04:24 PM, Rasmus Schultz wrote: This was just an example of a value object - for example: class HttpMethod { private $method; protected function __construct($method) { $this->method = $method; } public function getMethod() { return $this->method; } public static function get() { return new self("GET"); } public static function post() { return new self("POST"); } // ... } You could picture using a flyweight constructor inside those factory methods maybe, and probably other patterns... I think there's plenty of cases for "named constructors", this is just the PHP variety of that. There are also cases (such as this one) where only certain constructions are permitted - allowing any string for HTTP method (e.g. wrong names, wrong case etc.) is prevented by protecting the constructor... A possibly uglier but useful case would be where you have a large number of properties in an annotation and want to pass them in by name, but not using an anonymous array. (See also, my earlier Drupal examples.) To wit: <> class Node { public static function definition() { $def = new NodeDefinition(); $def->name = "node"; $def->label = "A Node"; $def->addLink('foo', 'bar'); // ... return $def; } // The rest of the node class here. } $annotations = $class_reflection->getAnnotations(); print get_class($annotations[0]); // prints "NodeDefinition". Or would that fail because it's not returning a self instance, but of another class? Rasmus, let me know when you have a proposal together and I'll try to dig up time to try rendering Drupal annotations in it again. :-) --Larry Garfield -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php