rfc:annotations

Differences

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

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
rfc:annotations [2010/09/08 12:51] pierrickrfc:annotations [2017/09/22 13:28] (current) – external edit 127.0.0.1
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
- 
  
 ===== Introduction ===== ===== Introduction =====
Line 16: Line 16:
  
   * **phpUnit** Providing meta functionality for test cases, examples: @dataProvider for test data iteration, @expectedException for catching exceptions, etc.   * **phpUnit** Providing meta functionality for test cases, examples: @dataProvider for test data iteration, @expectedException for catching exceptions, etc.
-  * **phpDoc** Providing useful information for API generation, examples: @author, @param, @return, etc. 
   * **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.
 +  * **TYPO3** for dependency injection and validation
 +  * **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.
  
 So, any meta mapping injection could be easily achieved via the implementation of a centralized Annotations support. So, any meta mapping injection could be easily achieved via the implementation of a centralized Annotations support.
 +
 +The .NET framework uses Data Annotation:
 +[[http://www.asp.net/mvc/tutorials/validation-with-the-data-annotation-validators-cs]]
 +
 +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 exist in these languages to provide hints to the compiler (@NotNull).
 +
 +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): 
 + 
 +[[http://willcode4beer.com/design.jsp?set=annotations_gotchas_best_practices]] 
 + 
 +[[http://www.softwarereality.com/programming/annotations.jsp]] 
 ===== Proposal ===== ===== Proposal =====
  
Line 36: 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;
  
-[Foo(true)]+    public function __construct($value) 
 +    { 
 +        $this->value = $value; 
 +    } 
 +
 + 
 +<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 107: 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, which are required to be public+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 ===== 
  
-Annotations are only useful if defined information can be retrieved somehow+Please notice that Annotations can also take advantage of "use" and "namespace" definitions
-There are different ways to retrieve annotation information, mostly differing from how it was defined.+Example:
  
-** The following explanation is only valid for classes **+<code php> 
 +namespace Foo\Bar;
  
-To define an Annotation that may be exported to subclasses, it is required to define an Annotation on ReflectionAnnotation subclass, called Inherited+<Exception("I should not do this")> 
-This is the general rule for Inherited annotations:+<\Exception("And also this one is ugly")> 
 +class Playground { 
 +    // ..
 +}
  
-** [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. **+// Exception points to \Foo\Bar\Exception 
 +// \Exception points to \Exception 
 +</code>
  
 +===== 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 156: 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 196: 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 224: 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 238: Line 273:
  
 class ReflectionMethod { class ReflectionMethod {
 +    // ...
 +
 +    public function getAnnotations();
 +    public function getAnnotation($name);
 +    public function hasAnnotation($name);
 +}
 +
 +class ReflectionParameter {
     // ...     // ...
  
Line 248: 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 261: 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.1283950318.txt.gz · Last modified: 2017/09/22 13:28 (external edit)