====== PHP RFC: Attribute Amendments ======
* Version: 1.2
* Date: 2020-05-11
* Author: Benjamin Eberlei (beberlei@php.net), Martin Schröder
* Status: Implemented
* Target: 8.0
* First Published at: http://wiki.php.net/rfc/attribute_amendments
* Patch: https://github.com/php/php-src/pull/5751
===== Introduction =====
This RFC discusses a few amendments to the [[https://wiki.php.net/rfc/attributes_v2|original Attributes RFC]] that was accepted for PHP 8.
===== Rename PhpAttribute class to Attribute =====
Important Context: Since the acceptence of Attributes, the PhpCompilerAttribute and PhpAttribute classes were unified to be only PhpAttribute. This was needed because the previous approach of disallowing the use of <> in userland would break "stub" generation that static analysis and IDEs rely on to fill their type database of internal functions/classes. Without it there was no functionality left in PhpCompilerAttribute to preserve.
The original RFC introduced a //PhpAttribute// class to be added to new
userland attributes. This name was inspired by //PhpToken// and to potentially
avoid breaking existing userland codebases with a class called //Attribute//.
However the //Php// prefix makes no sense for the attribute class compared to //PhpToken//.
In absence of a namespace policy, the global namespace is PHPs namespace. The
documentation states as much and the vote on https://wiki.php.net/rfc/php-namespace-in-core confirmed this.
Therefore we propose to rename //PhpAttribute// to be just //Attribute// and in addition
recommend that all internal/compiler attributes should be placed under the global namespace as well.
Extensions providing non-core attributes should consider using their own namespace, but this RFC makes no recommendation or rule about this.
===== Group statement for Attributes =====
Right now each attribute requires to be surrounded by the T_SL/T_SR tokens:
<><>
class Example
{
<>
<>
public function test()
{
}
}
We propose to allow a second syntax/style to group attributes in one declaration:
<>
class Example
{
<>
public function test()
{
}
}
In line with many other recent RFCs, trailing commas will also be possible in an attribute group declarations:
<<
Attr1("foo"),
Attr2("bar"),
>>
public function test()
{
}
This feature would be superseded by any other RFC getting accepted that changes the syntax.
===== Validate Attribute Target Declarations =====
At the moment an attribute can be put on any kind of declaration and the
developer of the attribute has no way of restricting the usage to only a subset
of declarations. This functionality is only available to internal attributes for now.
As an example, a developer might introduce an attribute to declare what
database table a class is stored in:
use ORM\Attributes\Table;
<>
class User
{
}
But they cannot prevent the Table attribute being used on another declaration
where it semantically does not belong:
use ORM\Attributes\Table;
class User
{
<>
public $id;
}
We propose that you can optionally restrict the declarations an attribute can be used on
by setting an optional argument to //PhpAttribute// (or //Attribute//).
namespace ORM\Attributes;
<>
class Table
{
}
The following constants will be added to //PhpAttribute//:
class PhpAttribute
{
public const int TARGET_CLASS = 1;
public const int TARGET_FUNCTION = (1 << 1);
public const int TARGET_METHOD = (1 << 2);
public const int TARGET_PROPERTY = (1 << 3);
public const int TARGET_CLASS_CONSTANT = (1 << 4);
public const int TARGET_PARAMETER = (1 << 5);
public const int TARGET_ALL = ((1 << 6) - 1);
public function __construct(int $flags = self::TARGET_ALL)
{
}
}
Important note: The target definition of an attribute is validated during the call to //ReflectionAttribute::newInstance//.
In fact it does not influence a call to //Reflection*::getAttributes()// and //ReflectionAttribute// instances can be returned
from this method, that are not valid on the reflected declaration. This is in line with the deferred validation of userland attributes
that the original RFC championed.
===== Validate Attribute Repeatability =====
At the moment every attribute can be repeated multiple times on a declaration.
For many use cases of attributes this is not desired.
For this reason we propose that by default attributes are not repeatable, and only
if the //PhpAttribute// has the flag //IS_REPEATABLE//
should it be possible to use it multiple times on the same declaration:
class PhpAttribute
{
public const int IS_REPEATABLE = ((1 << 10));
}
<>
class Route
{
}
class HomepageController
{
<>
<>
public function indexAction()
{
}
}
Important note: The repeatable flag of an attribute is validated during the call to //ReflectionAttribute::newInstance//.
In fact it does not influence a call to //Reflection*::getAttributes()// and //ReflectionAttribute// instances can be returned
from this method, that are not valid on the reflected declaration. This is in line with the deferred validation of userland attributes
that the original RFC championed.
===== Backward Incompatible Changes =====
Introducing a class //Attribute// into the global namespace is certainly going to break at least a handful of applications using this class name.
===== Proposed PHP Version(s) =====
8.0
===== RFC Impact =====
==== To SAPIs ====
none
==== To Existing Extensions ====
none
==== To Opcache ====
none
===== Open Issues =====
none
===== Vote =====
Voting will end on June 22nd, 2020 - 8:00 UTC.
==== Rename PhpAttribute class to Attribute ====
* Yes
* No
==== Group statement for Attributes ====
* Yes
* No
==== Validate Attribute Target Declarations ====
* Yes
* No
==== Validate Attribute Repeatability ====
* Yes
* No
===== Patches and Tests =====
- https://github.com/php/php-src/pull/5751
===== References =====
- [[https://wiki.php.net/rfc/attributes_v2|Attributes RFC]]
===== Updates =====
- 1.0 Initial RFC (11.5.2020)
- 1.1 Attributes\Attribute namespace (28.5.2020)
- 1.2 Revert Attributes\Attribute namespace, make IS_REPEATABLE flag on PhpAttribute (4.6.2020)