An attribute (PHP 8.0) attaches structured metadata to a declaration. The engine ignores most attributes and programs read them through Reflection, while a few built-in ones change how the engine behaves (manual).
Syntax
attribute-group: #[ attribute {, attribute} [,] ]
attribute: name [ ( arguments ) ]
#[MyAttribute] // no arguments
#[\MyExample\MyAttribute] // fully qualified name
#[MyAttribute(1234)] // positional argument
#[MyAttribute(value: 1234)] // named argument
#[MyAttribute(MyAttribute::VALUE)] // class constant
#[MyAttribute(100 + 200)] // constant expression
#[First, Second(2)] // two attributes in one group
class Thing {}
- An attribute group goes before the declaration, before its modifiers. Several groups can be stacked.
- The name resolves to a class, by the normal name resolution rules. The name may be unqualified, qualified or fully qualified.
- Arguments are optional, and must be constant expressions. Positional and named arguments are both allowed. Since PHP 8.5 static closures and first-class callables are allowed too.
- The syntax is also a comment in older PHP versions, if the group is on a line of
its own: PHP 7 reads
#[...]as a#comment.
Targets
Attributes can be placed on classes (including interfaces, traits, enums and anonymous classes), functions, methods, closures, arrow functions, parameters, properties, class constants, enum cases and, since PHP 8.5, global constants.
#[A] function f(#[A] $param) {}
#[A] class C {
#[A] public int $x;
#[A] const K = 1;
#[A] public function m() {}
}
enum E { #[A] case Z; }
$o = new #[A] class {};
$f = #[A] function () {};
$g = #[A] fn() => 1;
#[A] const G = 1; // PHP 8.5
An attribute on a promoted constructor parameter applies to both the parameter and the property.
Declaring an attribute class
Any class marked with #[Attribute] can be used as an attribute. The
attribute’s arguments are the constructor’s.
#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
final class Route
{
public function __construct(
public string $path,
public array $methods = ['GET'],
) {}
}
class Pages
{
#[Route('/home')]
#[Route('/start', methods: ['GET', 'HEAD'])]
public function home(): string { return 'hello'; }
}
| Flag | Value | Allows |
|---|---|---|
Attribute::TARGET_CLASS | 1 | Classes, interfaces, traits and enums |
Attribute::TARGET_FUNCTION | 2 | Functions and closures |
Attribute::TARGET_METHOD | 4 | Methods |
Attribute::TARGET_PROPERTY | 8 | Properties |
Attribute::TARGET_CLASS_CONSTANT | 16 | Class constants and enum cases |
Attribute::TARGET_PARAMETER | 32 | Parameters |
Attribute::TARGET_CONSTANT | 64 | Global constants (PHP 8.5) |
Attribute::TARGET_ALL | Sum of the above | All targets (the default) |
Attribute::IS_REPEATABLE | 128 | Using the attribute more than once on a declaration |
The flags are checked when the attribute is instantiated, not when the code is
compiled, for attributes defined in PHP code. Built-in attributes check their targets at
compile time. #[\DelayedTargetValidation] (PHP 8.5) defers that to
newInstance() as well.
Reading attributes
Reflection classes for classes, methods, functions, parameters, properties and
constants have a getAttributes() method. It returns
ReflectionAttribute objects, and an optional argument filters by class
name.
$method = new ReflectionMethod(Pages::class, 'home');
foreach ($method->getAttributes(Route::class) as $attribute) {
echo $attribute->getName(); // Route
var_dump($attribute->getArguments()); // the arguments as written
$route = $attribute->newInstance(); // calls the constructor
}
getName()andgetArguments()do not load the attribute class. OnlynewInstance()does, and that is where a missing class, a wrong target or bad arguments cause anError.- Class attributes are not inherited, so reflecting a subclass does not show its parent’s.
Built-in attributes
| Attribute | Since | Applies to | Effect |
|---|---|---|---|
#[\Attribute] | 8.0 | Classes | Marks a class as an attribute |
#[\ReturnTypeWillChange] | 8.1 | Methods | Silences the deprecation when overriding an internal method with an incompatible return type |
#[\AllowDynamicProperties] | 8.2 | Classes | Allows dynamic properties without a deprecation |
#[\SensitiveParameter] | 8.2 | Parameters | Replaces the value with a SensitiveParameterValue in stack traces. The function receives the real value |
#[\Override] | 8.3 | Methods; properties since 8.5 | A compile error if nothing is overridden |
#[\Deprecated] | 8.4 | Functions, methods, class constants; constants and traits since 8.5 | Emits E_USER_DEPRECATED on use. Takes optional message and since |
#[\NoDiscard] | 8.5 | Functions and methods | Warns when a return value is unused. Takes an optional message |
#[\DelayedTargetValidation] | 8.5 | Any | Moves the check of a built-in attribute’s target from compile time to newInstance() |
NoDiscard and the void cast
Since PHP 8.5, ignoring the return value of a function marked
#[\NoDiscard] gives a warning. A (void) cast states that the
value is deliberately discarded.
#[\NoDiscard('as the commit may fail')]
function commit(): bool { /* ... */ }
commit(); // warning
(void) commit(); // no warning
$ok = commit(); // no warning