rfc:annotations

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
rfc:annotations [2010/11/16 13:28]
jbondc Added links to good/bad use cases of annotations
rfc:annotations [2017/09/22 13:28] (current)
Line 1: Line 1:
 ====== Request for Comments: Class Metadata ====== ====== Request for Comments: Class Metadata ======
 +
   * Version: 1.0   * Version: 1.0
   * Date: 2010-08-24   * Date: 2010-08-24
   * Author: Guilherme Blanco <​guilhermeblanco@hotmail.com>,​ Pierrick Charron <​pierrick@php.net>​   * Author: Guilherme Blanco <​guilhermeblanco@hotmail.com>,​ Pierrick Charron <​pierrick@php.net>​
-  * Status: ​Ready for discussion+  * Status: ​Declined
   * First Published at: http://​wiki.php.net/​rfc/​annotations   * First Published at: http://​wiki.php.net/​rfc/​annotations
  
Line 17: Line 18:
   * **Doctrine** For Object-Relational mapping, examples: @Entity, @OneToOne, @Id, etc.   * **Doctrine** For Object-Relational mapping, examples: @Entity, @OneToOne, @Id, etc.
   * **Zend Framework Server classes** Used to automate mappings for XML-RPC, SOAP, etc.   * **Zend Framework Server classes** Used to automate mappings for XML-RPC, SOAP, etc.
-  * **FLOW3** for dependency injection and validation+  * **TYPO3** for dependency injection and validation
   * **Symfony2** for routing rules   * **Symfony2** for routing rules
   * **Others** One clear thing that comes to my mind is Validation, Functional Behavior injection (which could take advantage of [[rfc:​Traits]]),​ etc. Also, any Framework could take advantage of it somehow.   * **Others** One clear thing that comes to my mind is Validation, Functional Behavior injection (which could take advantage of [[rfc:​Traits]]),​ etc. Also, any Framework could take advantage of it somehow.
Line 28: Line 29:
 An advantage here is the .net framework will process some annotations and inject behavior into the compiled source code. An advantage here is the .net framework will process some annotations and inject behavior into the compiled source code.
  
-It's important to note that annotations exist in java and .net but many strong use cases of exists ​in these languages to provides ​hints to the compiler (@NotNull).+It's important to note that annotations exist in java and .net but many strong use cases exist in these languages to provide ​hints to the compiler (@NotNull).
  
-These use cases are not presented in this RFC (hints to the Zend lexer/​parser or other PHP implementations).+These types of use cases (hints to the Zend lexer/​parser or other PHP implementations) ​are not presented in this RFC.
  
 ===== Common Misconceptions ===== ===== Common Misconceptions =====
  
 Metadata mapping is commonly referred an feature that cannot be used widely, so its implementation is useless. Metadata mapping is commonly referred an feature that cannot be used widely, so its implementation is useless.
-As pointed previously, there'​re ​many use cases for this support. ​+As pointed previously, there are many use cases for this support. ​
  
 Though useful, the good and bad use cases of annotations are heavily debated (religiously):​ Though useful, the good and bad use cases of annotations are heavily debated (religiously):​
Line 52: Line 53:
 When using meta mapping, less characters is preferred to speed up its construction. When using meta mapping, less characters is preferred to speed up its construction.
  
-PHP Annotation could be simplified into this EBNF:+PHP Annotation could be simplified into this EBNF (Extended Backus-Naur Form):
  
 <​code>​ <​code>​
 Annotations ​    ::= Annotation {Annotation}* Annotations ​    ::= Annotation {Annotation}*
-Annotation ​     ::= "[" AnnotationName ["​("​ [Values] "​)"​] "]+Annotation ​     ::= "<" AnnotationName ["​("​ [Values] "​)"​] ">
-AnnotationName ​ ::=  QualifiedName | SimpleName ​| AliasedName+AnnotationName ​ ::=  QualifiedName | SimpleName
 QualifiedName ​  ::= {"​\"​}* NameSpacePart "​\"​ {NameSpacePart "​\"​}* SimpleName QualifiedName ​  ::= {"​\"​}* NameSpacePart "​\"​ {NameSpacePart "​\"​}* SimpleName
-AliasedName ​    ::= Alias ":"​ SimpleName 
 NameSpacePart ​  ::= identifier NameSpacePart ​  ::= identifier
 SimpleName ​     ::= identifier SimpleName ​     ::= identifier
 Alias           ::= identifier Alias           ::= identifier
-Values ​         ::= Array | Value {","​ Value}* +Values ​         ::= Value {","​ Value}* 
-Value           ::= PlainValue | FieldAssignment +Value           ::= integer | string | float | boolean | Array | Annotation
-PlainValue ​     ​::= integer | string | float | boolean | Array | Annotation +
-FieldAssignment ::= FieldName "​="​ PlainValue +
-FieldName ​      ::= identifier+
 Array           ::= "​array("​ ArrayEntry {","​ ArrayEntry}* "​)"​ Array           ::= "​array("​ ArrayEntry {","​ ArrayEntry}* "​)"​
 ArrayEntry ​     ::= Value | KeyValuePair ArrayEntry ​     ::= Value | KeyValuePair
-KeyValuePair ​   ::= Key "​=>" ​PlainValue+KeyValuePair ​   ::= Key "​=>" ​Value
 Key             ::= string | integer Key             ::= string | integer
 </​code>​ </​code>​
  
-With identifier being the terminal: [a-zA-Z_][a-zA-Z0-9_]* +With integer, string, float, boolean, float and identifier being the terminals. Identifier: [a-zA-Z_][a-zA-Z0-9_]* 
-The annotation can be a composite of a start/end tokens. Here is an example of PHP Annotations:​+ 
 +The start/end tokens ​are not closed, they can be changed if anyone desiresCurrently the chosen ones are: "<"​ for start token and ">"​ for end. 
 +Here is an example of PHP Annotations:​
  
 <​code>​ <​code>​
-[Entity(tableName="​users"​)]+<Entity("​users"​)>
 class User class User
 { {
-    ​[Column(type="​integer"​)] +    ​<Column("​integer"​)> 
-    ​[Id] +    ​<Id> 
-    ​[GeneratedValue(strategy="​AUTO"​)]+    ​<GeneratedValue("​AUTO"​)>
     protected $id;     protected $id;
  
     // ...     // ...
  
-    ​[ManyToMany(targetEntity="​Phonenumber"​)+    ​<ManyToMany("​Phonenumber"​)>
-    [JoinTable( +
-        name="​users_phonenumbers",​ +
-        joinColumns=array( +
-            [JoinColumn(name="​user_id",​ referencedColumnName="​id"​)] +
-        ), +
-        inverseJoinColumns=array( +
-            [JoinColumn(name="​phonenumber_id",​ referencedColumnName="​id",​ unique=true)] +
-        ) +
-    )]+
     protected $Phonenumbers;​     protected $Phonenumbers;​
 +}
 +</​code>​
 +
 +One point to notice is that nested Annotations are allowed. This is an important feature against key=>​value pair of what docblock implementations currently define. ​
 +Here is a simple sample of usage:
 +
 +<code php>
 +<​Simple(<​Example>​)>​
 +class User {
 +    // ...
 } }
 </​code>​ </​code>​
  
 The support is all done through the inclusion of a new class: ReflectionAnnotation. The support is all done through the inclusion of a new class: ReflectionAnnotation.
 +
 +
 ===== How to define Annotations ===== ===== How to define Annotations =====
  
-Annotations can be defined ​in classes, methods, properties ​or functions. +Annotations can be defined ​on classes, methods, propertiesfunctions ​or function parameters
-ReflectionAnnotation is an abstract class the must be implemented in order to accept an Annotation definition. +ReflectionAnnotation is an interface that must be implemented in order to accept an Annotation definition. 
-Once this class is extended, the subclass is ready to be used an an Annotation:+Once this class is extended, the subclass is ready to be used as an Annotation:
  
 <code php> <code php>
-class Foo extends ​\ReflectionAnnotation {}+class Foo implements ​\ReflectionAnnotation { 
 +    public $value; 
 + 
 +    public function __construct($value) 
 +    { 
 +        $this->​value = $value; 
 +    } 
 +}
  
-[Foo(true)]+<Foo(array(true))>
 class Bar { /* ... */ } class Bar { /* ... */ }
 </​code>​ </​code>​
  
-Just extending the base class already allows you to define a unique valueaccessible ​through ​a public property named "​value"​.+To have access ​to an Annotation instanceit is done through ​Reflection API.
  
 <code php> <code php>
Line 122: Line 131:
 $reflAnnot = $reflClass->​getAnnotation('​Foo'​);​ $reflAnnot = $reflClass->​getAnnotation('​Foo'​);​
  
-echo $foo->​value;​ // true+echo $foo->​value;​ // array(true)
 </​code>​ </​code>​
  
 To expand Annotations support, it is allowed to define other properties. To expand Annotations support, it is allowed to define other properties.
-BY doing it, you can define Annotations and automatically defining values to them.+By doing it, you can define Annotations and automatically defining values to constructor.
  
 <code php> <code php>
 namespace App\Annotation;​ namespace App\Annotation;​
  
-class Link extends ​\ReflectionAnnotation {+class Link implements ​\ReflectionAnnotation {
     public $url;     public $url;
     public $target;     public $target;
 +    ​
 +    public function __construct($url,​ $target = null)
 +    {
 +        $this->​url = $url;
 +        $this->​target = $target;
 +    }
 } }
  
-[App\Annotation\Link(url="​http://​www.php.net", ​target="​_blank"​)]+namespace ​App
 + 
 +<Annotation\Link("​http://​www.php.net",​ "​_blank"​)>
 class PHPWebsite { class PHPWebsite {
-    /... */+    /...
 } }
 </​code>​ </​code>​
  
-===== How to retrieve Annotation information =====+Please notice that Annotations can also take advantage of "​use"​ and "​namespace"​ definitions. 
 +Example:
  
-Annotations are only useful if defined information can be retrieved somehow. +<code php> 
-There are different ways to retrieve annotation information,​ mostly differing from how it was defined.+namespace Foo\Bar;
  
-** The following explanation ​is only valid for classes **+<​Exception("​I should not do this"​)>​ 
 +<​\Exception("​And also this one is ugly"​)>​ 
 +class Playground { 
 +    // ... 
 +}
  
-To define an Annotation that may be exported ​to subclasses, it is required ​to define an Annotation on ReflectionAnnotation subclass, called Inherited. +// Exception points ​to \Foo\Bar\Exception 
-This is the general rule for Inherited annotations:​+// \Exception points ​to \Exception 
 +</​code>​
  
-** [Inherited] annotations are not inherited when used to annotate anything other than a class. A class that implements one or more interfaces never inherits any annotations from the interfaces it implements. **+===== How to retrieve Annotation information =====
  
 +Annotations are only useful if defined information can be retrieved somehow.
 Example: Example:
  
 <code php> <code php>
-[Inherited] +class Author implements ​\ReflectionAnnotation { 
-class Foo extends ​\ReflectionAnnotation {}+    public $name; 
 +     
 +    public function __construct($name) { $this->​name = $name; } 
 +}
  
-class Bar extends \ReflectionAnnotation ​{} +<​Author("​Pierrick Charron"​)>​ 
- +class MyTest ​
-[Foo] +    <​Author("​Guilherme Blanco"​)>​ 
-[Bar] +    ​public function __toString() 
-class A {}+    { 
 +        // ... 
 +    } 
 +}
  
-class extends ​{}+class ExtendedTest ​extends ​MyTest ​{}
 </​code>​ </​code>​
  
Line 172: Line 202:
  
 <code php> <code php>
-$reflClassA = new \ReflectionClass('​A');+$reflClassA = new \ReflectionClass('​MyTest');
 var_dump($reflClassA->​getAnnotations());​ var_dump($reflClassA->​getAnnotations());​
 /* /*
-array(2) { +array(1) { 
-  ["Foo"​]=>​ +  ["Author"​]=>​ 
-  object(Foo)#%d (1) { +  object(Author)#%d (1) { 
-    ["value"​]=>​NULL +    ["name"​]=> ​string ​(16) "Pierrick Charron"
-  }, +
-  ["​Bar"​]=>​ +
-  object(Bar)#%d (1) { +
-    ["value"]=>NULL+
   }   }
 } }
 */ */
  
-$reflClassB ​new \ReflectionClass('B'); +$reflMethodToString ​$reflClassA->​getMethod('__toString'); 
-var_dump($reflClassB->​getAnnotations());​+var_dump($reflMethodToString->​getAnnotations());​
 /* /*
-array(2) { +array(1) { 
-  ["Foo"​]=>​ +  ["Author"​]=>​ 
-  object(Foo)#%d (1) { +  object(Author)#%d (1) { 
-    ["value"​]=>​NULL+    ["name"​]=> ​string (16) "​Guilherme Blanco"​
   }   }
 +}
 +*/
 +
 +$reflClassB = new \ReflectionClass('​ExtendedTest'​);​
 +var_dump($reflClassB->​getAnnotations());​
 +/*
 +array(0) {
 } }
 */ */
 </​code>​ </​code>​
  
-The method `getAnnotations()` supports one of the three arguments:+Please notice that multiple instantiation ​of same Annotation is left intentionally for discussion. It could be supported. 
 +Also, the inheritance of Annotations is left for discussion too. This can be done by 2 different approaches:
  
-  * \ReflectionAnnotation::ALL - Fetched both defined annotations + inherited ones +  * Using an Annotation at the top of ReflectionAnnotation ​definition. This was it was working on first patch. 
-  * \ReflectionAnnotation::​INHERITED - Retrieves only inherited annotations +  * Using a different interface to be implemented. This was another approach that we considered, but left for discussion.
-  * \ReflectionAnnotation::​DEFINED - Retrieves only defined annotations (which may be defined as [Inherited], but declared in current class)+
  
 Another available method is to retrieve an specific Annotation: `getAnnotation($name)`,​ which may return the matched Annotation or null if not found. Another available method is to retrieve an specific Annotation: `getAnnotation($name)`,​ which may return the matched Annotation or null if not found.
Line 212: Line 245:
  
 <code php> <code php>
- +interface ​ReflectionAnnotation {
-abstract class ReflectionAnnotation { +
-    const INHERITED = 1; +
-    const DECLARED ​ = 2; +
-    const ALL       = 3; +
- +
-    public $value ​  = null; +
- +
-    public function __construct(Reflector $reflector, array $properties = null) { +
-        if (is_array($properties)) { +
-            foreach ($properties as $k => $v) { +
-                $this->​$k = $v; +
-            } +
-        } +
-    }+
 } }
  
Line 240: Line 259:
     // ...     // ...
  
-    public function getAnnotations($type = ReflectionAnnotation::​ALL); +    public function getAnnotations();​ 
-    public function getAnnotation($name, $type = ReflectionAnnotation::​ALL); +    public function getAnnotation($name);​ 
-    public function hasAnnotation($name, $type = ReflectionAnnotation::​ALL);+    public function hasAnnotation($name);​
 } }
  
Line 254: Line 273:
  
 class ReflectionMethod { class ReflectionMethod {
 +    // ...
 +
 +    public function getAnnotations();​
 +    public function getAnnotation($name);​
 +    public function hasAnnotation($name);​
 +}
 +
 +class ReflectionParameter {
     // ...     // ...
  
Line 264: Line 291:
 ===== BC breaks ===== ===== BC breaks =====
  
-   * Creates ​two additional classes named "​ReflectionAnnotation" and "​Inherited" that may break existing code.+   * Creates ​one additional classes named "​ReflectionAnnotation"​ that may break existing code.
    * None otherwise (no new keywords)    * None otherwise (no new keywords)
  
 +===== To be discussed =====
 +
 +  * Possible usage of "​annotation"​ as keyword instead of an abstract class.
 +  * Tokens for start/end. Currently it's "<"​ and ">"​.
 +  * Nested Annotation declaration:​ <​Foo(<​Bar>​)>​ or <Foo(new Bar)> (this changes the EBNF if any change is agreed). Currently the supported one is: <​Foo(<​Bar>​)>​
 +  * Multiple instantiations of Annotation classes on a same block.
 +  * Inheritance of classes/​properties/​method and Annotations declarations.
  
 ===== Patch ===== ===== Patch =====
  
-[[http://​www.adoy.net/​php/​Annotations.diff]]+Patch : [[http://​www.adoy.net/​php/​Annotations-v3.diff]]  
 + 
 +Tests : [[http://​www.adoy.net/​php/​Annotations-v3-tests.diff]]
  
 ===== Changelog ===== ===== Changelog =====
Line 277: Line 313:
   * 2010-08-24 guilhermeblanco Updated for a real doable support   * 2010-08-24 guilhermeblanco Updated for a real doable support
   * 2010-08-24 pierrick Add the patch   * 2010-08-24 pierrick Add the patch
 +  * 2011-05-09 guilhermeblanco Updated patch with recent compatibility. Previous patch removed. New one should be added shortly.
rfc/annotations.1289914086.txt.gz · Last modified: 2017/09/22 13:28 (external edit)