rfc:attributes_v2
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionLast revisionBoth sides next revision | ||
rfc:attributes_v2 [2020/04/14 11:21] – beberlei | rfc:attributes_v2 [2020/05/04 12:00] – beberlei | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== PHP RFC: Attributes v2 ====== | ====== PHP RFC: Attributes v2 ====== | ||
- | * Version: 0.4 | + | * Version: 0.5 |
* Date: 2020-03-09 | * Date: 2020-03-09 | ||
* Author: Benjamin Eberlei (beberlei@php.net), | * Author: Benjamin Eberlei (beberlei@php.net), | ||
- | * Status: | + | * Status: |
+ | * Target: 8.0 | ||
* First Published at: http:// | * First Published at: http:// | ||
+ | * Implementation: | ||
Large credit for this RFC goes to Dmitry Stogov whose previous work on | Large credit for this RFC goes to Dmitry Stogov whose previous work on | ||
Line 46: | Line 48: | ||
* class methods | * class methods | ||
* function/ | * function/ | ||
+ | |||
+ | Examples: | ||
+ | |||
+ | <code php> | ||
+ | << | ||
+ | class Foo | ||
+ | { | ||
+ | << | ||
+ | public const FOO = ' | ||
+ | |||
+ | << | ||
+ | public $x; | ||
+ | |||
+ | << | ||
+ | public function foo(<< | ||
+ | } | ||
+ | |||
+ | $object = new << | ||
+ | |||
+ | << | ||
+ | function f1() { } | ||
+ | |||
+ | $f2 = << | ||
+ | |||
+ | $f3 = << | ||
+ | </ | ||
Attributes are added before the declaration they belong to, similar to doc-block comments. They can be declared **before** or **after** a doc-block comment that documents a declaration. | Attributes are added before the declaration they belong to, similar to doc-block comments. They can be declared **before** or **after** a doc-block comment that documents a declaration. | ||
<code php> | <code php> | ||
- | <<...>> | + | <<ExampleAttribute>> |
- | <<...>> | + | /** docblock */ |
+ | <<AnotherExampleAttribute>> | ||
function foo() {} | function foo() {} | ||
</ | </ | ||
Line 70: | Line 99: | ||
The same attribute name can be used more than once on the same declaration. | The same attribute name can be used more than once on the same declaration. | ||
- | Sementically | + | Attributes can also be declared on the same line: |
+ | |||
+ | <code php> | ||
+ | << | ||
+ | function foo() {} | ||
+ | </ | ||
+ | |||
+ | Semantically | ||
with the attribute name and passing arguments to the constructor. | with the attribute name and passing arguments to the constructor. | ||
Line 89: | Line 125: | ||
function foo() {} | function foo() {} | ||
</ | </ | ||
+ | |||
+ | "The " | ||
See discussion about alternative syntaxes below for more info why the most | See discussion about alternative syntaxes below for more info why the most | ||
Line 166: | Line 204: | ||
zend_ce_php_compiler_attribute = zend_register_internal_class(& | zend_ce_php_compiler_attribute = zend_register_internal_class(& | ||
- | zend_attributes_internal_validator cb = zend_attribute_validate_phpcompilerattribute; | + | zend_compiler_attribute_register(zend_ce_php_compiler_attribute, |
- | zend_compiler_attribute_register(zend_ce_php_compiler_attribute, | + | |
</ | </ | ||
Line 208: | Line 245: | ||
The constant AST is resolved to a value when accessing attributes with the Reflection API. | The constant AST is resolved to a value when accessing attributes with the Reflection API. | ||
+ | **Note:** This is intentionally different from the previous Attributes RFC where an object | ||
+ | with ast\node was returned. | ||
+ | |||
+ | The parser understands the context to differentiate attributes from bitshifts in constant ASTs. | ||
+ | |||
+ | <code php> | ||
+ | << | ||
+ | function foo() {} | ||
+ | </ | ||
==== Reflection ==== | ==== Reflection ==== | ||
Line 234: | Line 280: | ||
<code php> | <code php> | ||
- | $attributes = $reflectionFunction-> | + | $attributes = $reflectionFunction-> |
+ | | ||
+ | | ||
+ | ); | ||
</ | </ | ||
Line 244: | Line 293: | ||
public function getName(): string | public function getName(): string | ||
public function getArguments(): | public function getArguments(): | ||
- | public function | + | public function |
} | } | ||
</ | </ | ||
Line 250: | Line 299: | ||
Because validation of attributes is only performed during | Because validation of attributes is only performed during | ||
- | // | + | // |
declare the attribute class. | declare the attribute class. | ||
from // | from // | ||
Line 279: | Line 328: | ||
var_dump($attributes[0]-> | var_dump($attributes[0]-> | ||
var_dump($attributes[0]-> | var_dump($attributes[0]-> | ||
- | var_dump($attributes[0]-> | + | var_dump($attributes[0]-> |
} | } | ||
Line 482: | Line 531: | ||
foreach ($attributes as $listenerAttribute) { | foreach ($attributes as $listenerAttribute) { | ||
/** @var $listener Listener */ | /** @var $listener Listener */ | ||
- | $listener = $listenerAttribute-> | + | $listener = $listenerAttribute-> |
// with $listener instanceof Listener attribute, | // with $listener instanceof Listener attribute, | ||
Line 520: | Line 569: | ||
Doctrine or any userland library can utilize the name filter with a parent class to fetch | Doctrine or any userland library can utilize the name filter with a parent class to fetch | ||
- | only attributes they are interested in: | + | only attributes they are interested in. With the flexibility in the proposed Reflection API, Doctrine (or any other userland |
+ | annotation/ | ||
+ | attributes by adding their own logic on top wihout PHP attributes getting in | ||
+ | the way. | ||
+ | |||
+ | Here is a complex example of an object using Doctrine Annotations and the proposed Attributes side by side to implement the same thing: | ||
<code php> | <code php> | ||
- | namespace | + | <?php |
+ | use Doctrine\ORM\Attributes as ORM; | ||
+ | use Symfony\Component\Validator\Constraints as Assert; | ||
- | abstract class Annotation { | + | << |
- | class TARGET_CLASS = 1; | + | /** @ORM\Entity */ |
- | class TARGET_PROPERTY = 2; | + | class User |
- | + | ||
- | public $target = self:: | + | |
- | + | ||
- | final public function __construct(array $values = []) { | + | |
- | foreach ($values as $key => $value) { | + | |
- | $this->$key = $value; | + | |
- | } | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | class AnnotationReader | + | |
{ | { | ||
- | | + | |
- | $doctrineAnnotations = []; | + | << |
+ | | ||
- | foreach ($reflection-> | + | /** |
- | // filter out any that doesn't extend Doctrine's annotation base class | + | * @ORM\Column(type=" |
- | | + | * @Assert\Email(message=" |
- | | + | */ |
- | } | + | << |
+ | << | ||
+ | | ||
- | $annotation | + | /** |
+ | * @ORM\Column(type=" | ||
+ | * @Assert\Range( | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | * ) | ||
+ | */ | ||
+ | << | ||
+ | << | ||
+ | protected $height; | ||
- | | + | |
- | // getClassAnnotations requires all annotations to be allowed on a class | + | * @ORM\ManyToMany(targetEntity="Phonenumber") |
- | if ($annotation-> | + | * @ORM\JoinTable(name=" |
- | throw new \RuntimeException(get_class($annotation) . " | + | |
- | | + | * inverseJoinColumns={@ORM\JoinColumn(name="phonenumber_id", referencedColumnName=" |
- | + | * ) | |
- | | + | */ |
- | } | + | << |
- | + | << | |
- | | + | << |
- | | + | |
+ | private $phonenumbers; | ||
} | } | ||
</ | </ | ||
- | With this flexibility in the Reflection API, Doctrine (or any other userland | + | The attributes approach is a bit limited, because it doesn' |
- | annotation/attributes | + | |
- | attributes by adding their own logic on top wihout | + | |
- | the way. | + | |
[[https:// | [[https:// | ||
Line 629: | Line 686: | ||
full Doctrine like system is not necessary for a lot of use-cases, especially | full Doctrine like system is not necessary for a lot of use-cases, especially | ||
the PHP internal use-cases. | the PHP internal use-cases. | ||
+ | |||
+ | === Why are nested attributes not allowed? === | ||
+ | |||
+ | Nesting attributes means, defining an attribute as an argument to another attribute. This is intentionally not allowed because it would mean an attribute can be declared within an argument, which at the moment this RFC defines as constant AST. A constant AST is already known and re-usable and describes a subset of expressions that can be used in property, constant or argument default value declarations. | ||
+ | |||
+ | Reusing constant AST concept for attribute arguments introduces consistency and the potential to benefit from future work on this concept. Allowing nested attributes would potentially cause a conflict in the future, as well as makes introducing complexity for users to understand this new context that behaves differently than other parts of the language. | ||
=== Naming (attributes or annotations) === | === Naming (attributes or annotations) === | ||
Line 687: | Line 750: | ||
* Other languages such as Go have simple but powerful serialization from XML/JSON to objects and back. The combination of typed properties an attributes puts this in reach for core or a PHP extension to implement. | * Other languages such as Go have simple but powerful serialization from XML/JSON to objects and back. The combination of typed properties an attributes puts this in reach for core or a PHP extension to implement. | ||
* An alternative " | * An alternative " | ||
- | * Extending userland attributes to allow declaring which target they are allowed to be declared on including validation of those targets in // | + | * Extending userland attributes to allow declaring which target they are allowed to be declared on including validation of those targets in // |
- | ===== Proposed | + | ===== Voting ===== |
- | * Accept PHP Attributes v2 into core? 2/3 majority | + | <doodle title=" |
- | * Which syntax to use for attributes? "<<>> | + | |
+ | * No | ||
+ | </ | ||
+ | |||
+ | Secondary vote (choice with the most votes is picked): | ||
+ | |||
+ | <doodle title=" | ||
+ | | ||
+ | | ||
+ | </ | ||
+ | |||
+ | Vote closes on May 4th, 12:00 UTC. | ||
===== Patches and Tests ===== | ===== Patches and Tests ===== | ||
Line 741: | Line 815: | ||
* Changed validation of compiler attributes to use a C callback instead of instantiating object | * Changed validation of compiler attributes to use a C callback instead of instantiating object | ||
* Offer alternative syntax " | * Offer alternative syntax " | ||
+ | |||
+ | 0.5: | ||
+ | |||
+ | * Rename ReflectionAttribute:: | ||
rfc/attributes_v2.txt · Last modified: 2020/08/01 23:38 by carusogabriel