rfc:attributes
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision | ||
rfc:attributes [2016/04/21 23:35] – dmitry | rfc:attributes [2016/05/12 04:51] – corrected typoin EBNF: s/name/namespace-name ocramius | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== PHP RFC: Attributes ====== | ====== PHP RFC: Attributes ====== | ||
- | * Version: | + | * Version: |
* Date: 2016-04-21 | * Date: 2016-04-21 | ||
* Author: Dmitry Stogov, dmitry@zend.com | * Author: Dmitry Stogov, dmitry@zend.com | ||
- | * Status: | + | * Status: |
* First Published at: http:// | * First Published at: http:// | ||
===== Introduction ===== | ===== Introduction ===== | ||
- | Attributes (or annotation) is a form of syntactic metadata that can be added to language classes, functions, etc. PHP offers only a single form of such metadata - doc-comments. This is just a string and to keep some structured information, | + | Attributes (or annotations) are a form of syntactic metadata that can be added to language classes, functions, etc. PHP offers only a single form of such metadata - doc-comments. This is just a string and to keep some structured information, |
- | Many languages like Java, C#, Hack, etc provide a simpler way. They allow definition of structured meta-information through small syntax extension. | + | Many languages like Java, C#, Hack, etc provide a simpler way. They allow the definition of structured meta-information through small syntax extension. |
===== Proposal ===== | ===== Proposal ===== | ||
==== Attribute Syntax ==== | ==== Attribute Syntax ==== | ||
- | Attribute | + | < |
<code php> | <code php> | ||
Line 22: | Line 22: | ||
</ | </ | ||
- | Each attribute definition construct may also define one or few named attributes, each attribute may be used without | + | Each attribute definition construct may also define one or more named attribute, which may be used with no value, |
< | < | ||
- | < | + | < |
- | { "," | + | { "," |
- | < | + | <namespace-name> |
< | < | ||
</ | </ | ||
Line 38: | Line 38: | ||
</ | </ | ||
- | It's not possible to use the same attribute name for the same definition | + | It's not possible to use the same attribute name more than once on the same definition, however, it's possible to use multiple attribute values |
<code php> | <code php> | ||
Line 50: | Line 50: | ||
==== Arbitrary PHP Expressions as Attribute Values (AST attributes) ==== | ==== Arbitrary PHP Expressions as Attribute Values (AST attributes) ==== | ||
- | Except for simple scalars, attribute values may be represented with any valid PHP expression. | + | Other than simple scalars, attribute values may be represented with any valid PHP expression. |
<code php> | <code php> | ||
Line 58: | Line 58: | ||
</ | </ | ||
- | In this case, internally, the value of attribute is kept as an Abstract Syntax Tree, and we will able to read every individual node of this tree separately. This approach implies usage of the same PHP syntax for meta data and eliminates need for separate parser. | + | In this case, internally, the value of the attribute is kept as an Abstract Syntax Tree, and the user will have the ability |
+ | |||
+ | The native usage of an AST is not especially necessary. It's also possible to use plain strings and then transform them into AST at user level, through the php-ast extension. | ||
+ | |||
+ | <code php> | ||
+ | << | ||
+ | function foo($a, $b) { | ||
+ | } | ||
+ | $r = new ReflectionFunction(" | ||
+ | $ast = ast\parse_code($r-> | ||
+ | </ | ||
==== Reflection ==== | ==== Reflection ==== | ||
- | Few reflection | + | Reflection |
<code php> | <code php> | ||
Line 68: | Line 78: | ||
function ReflectionClass:: | function ReflectionClass:: | ||
function ReflectionProperty:: | function ReflectionProperty:: | ||
- | function | + | function |
</ | </ | ||
- | These functions return | + | These functions return |
<code php> | <code php> | ||
Line 83: | Line 93: | ||
array(3) { | array(3) { | ||
[" | [" | ||
- | | + | |
+ | } | ||
[" | [" | ||
- | int(0) | + | |
+ | [0]=> | ||
+ | | ||
+ | } | ||
[" | [" | ||
array(2) { | array(2) { | ||
Line 97: | Line 111: | ||
==== AST Representation ==== | ==== AST Representation ==== | ||
- | While internally AST is stored in native zend_ast format, Reflection*:: | + | While internally AST is stored in native zend_ast format, Reflection*:: |
<code php> | <code php> | ||
Line 110: | Line 124: | ||
array(1) { | array(1) { | ||
[" | [" | ||
- | object(ast\Node)# | + | |
- | [" | + | [0]=> |
- | int(521) | + | |
- | [" | + | [" |
- | int(0) | + | int(521) |
- | [" | + | [" |
- | int(0) | + | int(0) |
- | [" | + | [" |
- | array(2) { | + | int(0) |
- | [0]=> | + | [" |
- | object(ast\Node)# | + | array(2) { |
- | [" | + | [0]=> |
- | int(520) | + | object(ast\Node)# |
- | [" | + | [" |
- | int(1) | + | int(520) |
- | [" | + | [" |
- | int(0) | + | int(1) |
- | [" | + | [" |
- | array(2) { | + | int(0) |
- | [0]=> | + | [" |
- | object(ast\Node)# | + | array(2) { |
- | [" | + | [0]=> |
- | int(256) | + | object(ast\Node)# |
- | [" | + | [" |
- | int(0) | + | int(256) |
- | [" | + | [" |
- | int(0) | + | int(0) |
- | [" | + | [" |
- | array(1) { | + | int(0) |
- | [0]=> | + | [" |
- | string(1) " | + | array(1) { |
+ | [0]=> | ||
+ | string(1) " | ||
+ | } | ||
} | } | ||
- | } | + | |
- | | + | object(ast\Node)# |
- | object(ast\Node)# | + | [" |
- | [" | + | int(256) |
- | int(256) | + | [" |
- | [" | + | int(0) |
- | int(0) | + | [" |
- | [" | + | int(0) |
- | int(0) | + | [" |
- | [" | + | array(1) { |
- | array(1) { | + | [0]=> |
- | [0]=> | + | string(1) " |
- | string(1) " | + | } |
} | } | ||
} | } | ||
} | } | ||
+ | [1]=> | ||
+ | int(0) | ||
} | } | ||
- | [1]=> | ||
- | int(0) | ||
} | } | ||
} | } | ||
} | } | ||
</ | </ | ||
+ | |||
+ | [[https:// | ||
==== Use Cases ==== | ==== Use Cases ==== | ||
- | With attributes it's extremely simple to mark some functions with some specific | + | With attributes, it's extremely simple to mark some functions with some specific |
<code php> | <code php> | ||
Line 179: | Line 198: | ||
function foo() { | function foo() { | ||
... | ... | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Attributes may be used as a base level for an annotation system similar to Doctrine, where each attribute is represented by an object of corresponding class that perform validation and other actions. | ||
+ | |||
+ | <code php> | ||
+ | <?php | ||
+ | namespace Doctrine\ORM { | ||
+ | |||
+ | class Entity { | ||
+ | private $name; | ||
+ | public function __construct($name) { | ||
+ | $this-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function GetClassAttributes($class_name) { | ||
+ | $reflClass = new \ReflectionClass($class_name); | ||
+ | $attrs = $reflClass-> | ||
+ | foreach ($attrs as $name => & | ||
+ | $name = " | ||
+ | $values = new $name(...$values); | ||
+ | } | ||
+ | return $attrs; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | namespace { | ||
+ | << | ||
+ | class User {} | ||
+ | |||
+ | var_dump(Doctrine\ORM\GetClassAttributes(" | ||
+ | } | ||
+ | ?> | ||
+ | </ | ||
+ | |||
+ | < | ||
+ | array(1) { | ||
+ | [" | ||
+ | object(Doctrine\ORM\Entity)# | ||
+ | [" | ||
+ | string(4) " | ||
+ | } | ||
} | } | ||
</ | </ | ||
Line 204: | Line 266: | ||
</ | </ | ||
- | ==== Criticism and Alternative Approach | + | ==== Special Attributes |
- | Today we use single doc-comments for any kind of meta-information, | + | < |
+ | |||
+ | ==== Criticism and Alternative Approaches ==== | ||
+ | |||
+ | === Doc-comments === | ||
+ | |||
+ | Today we are using single doc-comments for any kind of meta-information, | ||
<code php> | <code php> | ||
Line 241: | Line 309: | ||
</ | </ | ||
- | This approach works, but PHP itself | + | This approach works, but PHP itself |
e.g. to check " | e.g. to check " | ||
It might be possible to make PHP parse existing doc-comments and keep information as structured attributes, but we would need to invoke additional parser for each doc-comment; | It might be possible to make PHP parse existing doc-comments and keep information as structured attributes, but we would need to invoke additional parser for each doc-comment; | ||
+ | |||
+ | === Full Featured Annotation System (like Doctrine) === | ||
+ | |||
+ | This RFC proposes only base PHP attribute functionality. It doesn' | ||
+ | |||
+ | <code php> | ||
+ | /** | ||
+ | * @Block( | ||
+ | * id = " | ||
+ | * | ||
+ | * ) | ||
+ | */ | ||
+ | |||
+ | << | ||
+ | " | ||
+ | " | ||
+ | ]))>> | ||
+ | class PageTitleBlock { | ||
+ | } | ||
+ | |||
+ | function TranslateDrupalAttribute($value) { | ||
+ | if ($value instanceof \ast\Node) { | ||
+ | if ($value-> | ||
+ | $a = $value-> | ||
+ | if (is_string($a) && class_exists($a)) { | ||
+ | $value = new $a; | ||
+ | } else if ($a instanceof \ast\Node && | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | $args = $a-> | ||
+ | if ($args instanceof ast\Node && $args-> | ||
+ | $obj = new $a-> | ||
+ | foreach ($args-> | ||
+ | if ($arg instanceof ast\Node && | ||
+ | $arg-> | ||
+ | count($arg-> | ||
+ | is_string($arg-> | ||
+ | $name = $arg-> | ||
+ | $val = $arg-> | ||
+ | if ($val instanceof ast\Node) { | ||
+ | $obj-> | ||
+ | } else { | ||
+ | $obj-> | ||
+ | } | ||
+ | } else { | ||
+ | throw DrupalAnnotationError(" | ||
+ | } | ||
+ | } | ||
+ | } else { | ||
+ | $name = $a-> | ||
+ | $obj = new $name($args); | ||
+ | } | ||
+ | $value = $obj; | ||
+ | } else { | ||
+ | throw DrupalAnnotationError(" | ||
+ | } | ||
+ | } else { | ||
+ | throw DrupalAnnotationError(" | ||
+ | } | ||
+ | } | ||
+ | return $value; | ||
+ | } | ||
+ | |||
+ | function GetDrupalAnnotations($class_name) { | ||
+ | $reflClass = new \ReflectionClass($class_name); | ||
+ | $attrs = $reflClass-> | ||
+ | $ret = []; | ||
+ | foreach ($attrs as $name => $values) { | ||
+ | if ($name == " | ||
+ | foreach ($values as & | ||
+ | $ret[] = TranslateDrupalAttribute($value); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | return $ret; | ||
+ | } | ||
+ | |||
+ | class Block {} | ||
+ | class Translation { | ||
+ | public $text; | ||
+ | function __construct($text) { | ||
+ | $this-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | var_dump(GetDrupalAnnotations(" | ||
+ | </ | ||
+ | |||
+ | < | ||
+ | array(1) { | ||
+ | [0]=> | ||
+ | object(Block)# | ||
+ | [" | ||
+ | string(21) " | ||
+ | [" | ||
+ | object(Translation)# | ||
+ | [" | ||
+ | string(13) "Site branding" | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | === ' | ||
+ | |||
+ | ' | ||
+ | |||
+ | === Naming (attributes or annotations) === | ||
+ | |||
+ | Different programming languages use different terms for similar features. Some use annotation, some attributes. I prefer name " | ||
===== Backward Incompatible Changes ===== | ===== Backward Incompatible Changes ===== | ||
- | None | + | The RFC doesn' |
===== Proposed PHP Version(s) ===== | ===== Proposed PHP Version(s) ===== | ||
Line 257: | Line 440: | ||
==== To Existing Extensions ==== | ==== To Existing Extensions ==== | ||
- | [[https:// | + | [[https:// |
==== To Opcache ==== | ==== To Opcache ==== | ||
Line 270: | Line 453: | ||
===== Open Issues ===== | ===== Open Issues ===== | ||
* part of patch related to new AST classes (zend_ast.*) might need to be slightly changed to satisfy need of attributes and php-ast in best way. | * part of patch related to new AST classes (zend_ast.*) might need to be slightly changed to satisfy need of attributes and php-ast in best way. | ||
- | * getAttributes() should return empty array in case of no attributes | + | * <del>getAttributes() should return empty array in case of no attributes</ |
- | * For each defined attribute getArray() should return a numerically indexed array independently of number of associated values. For attributes without values it should return empty arrays. | + | * <del>For each defined attribute getArray() should return a numerically indexed array independently of number of associated values. For attributes without values it should return empty arrays.</ |
- | * Attribute names might be namespace qualified e.g. << | + | * <del>Attribute names might be namespace qualified e.g. < |
- | * It may be useful to optionally allow some extra special character e.g. << | + | * <del>It may be useful to optionally allow some extra special character e.g. < |
+ | * < | ||
+ | * < | ||
+ | | ||
===== Proposed Voting Choices ===== | ===== Proposed Voting Choices ===== | ||
- | This RFC modifies the PHP language syntax | + | The voting started on May 10th, 2016 and will close on May 24th, 2016. |
+ | |||
+ | <doodle title=" | ||
+ | * Yes | ||
+ | * No | ||
+ | </ | ||
+ | |||
+ | ---- | ||
+ | |||
+ | <doodle title=" | ||
+ | * valid PHP expression (internally represented as AST) | ||
+ | * valid PHP constant (number or string) | ||
+ | </ | ||
===== Patches and Tests ===== | ===== Patches and Tests ===== | ||
Line 290: | Line 487: | ||
* [[https:// | * [[https:// | ||
* [[https:// | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
- | ===== Rejected Features ===== | ||
- | Keep this updated with features that were discussed on the mail lists. |
rfc/attributes.txt · Last modified: 2017/09/22 13:28 by 127.0.0.1