With the release of PHP 8.0, developers gained a long-awaited feature: Attributes (also known as annotations in other languages like Java or C#).
Attributes allow you to add metadata to classes, methods, properties, and other structures in a clean, structured way. Instead of relying on PHPDoc comments or external configuration, you can now attach additional information directly in the code and retrieve it at runtime.
What are Attributes?
Attributes are structured metadata that you can attach to PHP code elements such as:
This metadata can then be inspected at runtime using the Reflection API.
- Why do we need Attributes?
Before attributes, developers often used:
Example (Doctrine ORM using PHPDoc):
/** * @Entity * @Table(name="users") */ class User { /** @Column(type="string") */ private $name; }
With attributes, we can now write:
#[Entity] #[Table(name: "users")] class User { #[Column(type: "string")] private string $name; }
Cleaner, native, and type-safe.
Attribute Syntax
Attributes in PHP are written inside #[ ... ].
Example: Multiple Attributes
#[Route("/home")] #[Middleware("auth")] function homePage() { return "Welcome!"; }
Here:
- Attributes with Arguments
Attributes can take arguments, just like function calls.
#[Cache(ttl: 3600)] function getProducts() { // ... }
- Grouped Attributes
You can group multiple attributes in a single #[ ... ].
#[Route("/dashboard"), Middleware("auth")] function dashboard() { // ... }
This is equivalent to writing them separately.
Reading Attributes with the Reflection API
To use attributes effectively, you need to read them at runtime. For that, PHP provides new methods in the Reflection API.
- Example: Getting Method Attributes
#[Route("/home")] #[Middleware("auth")] function homePage() { return "Welcome!"; } $reflection = new ReflectionFunction('homePage'); $attributes = $reflection->getAttributes(); foreach ($attributes as $attribute) { echo "Attribute: " . $attribute->getName(); print_r($attribute->getArguments()); }
Output:
Attribute: Route Array ( [0] => /home ) Attribute: Middleware Array ( [0] => auth )
- Example: Getting Class Attributes
#[Entity] #[Table(name: "users")] class User {} $reflection = new ReflectionClass(User::class); foreach ($reflection->getAttributes() as $attribute) { echo $attribute->getName(); } // Entity // Table
Attribute Targets
You can specify where your attribute can be applied (class, method, property, etc.) using Attribute::TARGET_* constants.
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] class Route { public function __construct(public string $path) {} }
This means Route can be used only on classes and methods.
Source: Orkhan Alishov's notes