Understanding PHP Attributes: How to Use Metadata in Modern Development
Attributes (often referred to as annotations in other programming languages) were introduced in PHP 8.0. They allow developers to add structured, machine-readable metadata directly to classes, methods, properties, and parameters.
Before Attributes, developers relied on “docblocks” (PHPDoc comments) to specify metadata, which required parsing raw strings. Attributes provide a native, compile-time checked alternative.
Declaring an Attribute
To create an attribute, you define a standard PHP class and annotate it with the #[Attribute] marker.
#[Attribute]
class Route {
public function __construct(
public string $path,
public string $method = 'GET'
) {}
}
Applying Attributes
Once declared, you can apply your attribute to any target (like classes or methods) using the #[AttributeName] syntax.
class UserController {
#[Route('/users', method: 'GET')]
public function listUsers() {
// ...
}
}
Reading Attributes with Reflection
Attributes are not executed automatically. Instead, they are parsed at runtime using PHP’s Reflection API. Here is how you can read attributes from a class method:
$reflection = new ReflectionMethod(UserController::class, 'listUsers');
$attributes = $reflection->getAttributes(Route::class);
foreach ($attributes as $attribute) {
// Instantiate the attribute class
$route = $attribute->newInstance();
echo "Path: " . $route->path; // Output: /users
echo "Method: " . $route->method; // Output: GET
}
Why Use Attributes?
- Readability: Keep configuration close to the actual code it modifies.
- Type Safety: Unlike docblock comments, attributes are actual classes, meaning IDEs can autocomplete arguments and highlight syntax errors.
- Performance: Native parsing is much faster than parsing strings from comments.
Attributes are widely used in frameworks like Symfony (for routing and dependency injection) and Doctrine (for database mapping), and understanding how to construct and parse them is a key skill for advanced PHP developers.