Attributes — Native Metadata
PHP 8.0 replaced the docblock-annotation hack with proper, first-class attributes. They're real syntax, parseable by the engine, checked by static analysis, and validated at compile time.
// Define an attribute
#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
class Route {
public function __construct(
public string $path,
public string $method = 'GET',
) {}
}
// Use it — clean, native syntax
class UserController {
#[Route('/users', method: 'GET')]
public function index(): Response { /* ... */ }
#[Route('/users/{id}', method: 'GET')]
#[Route('/user/{id}', method: 'GET')] // Repeatable!
public function show(int $id): Response { /* ... */ }
#[Route('/users', method: 'POST')]
#[RequiresAuth(role: 'admin')]
public function create(): Response { /* ... */ }
}
// Read attributes via Reflection
$method = new ReflectionMethod(UserController::class, 'index');
$routes = $method->getAttributes(Route::class);
foreach ($routes as $attr) {
$route = $attr->newInstance(); // Route object with path and method
}
Attributes replaced the fragile docblock annotation ecosystem (Doctrine Annotations) with something that's part of the language. They support named arguments, validation via reflection, and target constraints. Frameworks like Symfony and Laravel adopted them immediately.
Significance: Standardization
Docblock annotations were a community hack that required third-party parsers and couldn't be validated by the engine. Native attributes are faster, safer, and standardized — every framework can rely on the same mechanism without shipping their own annotation parser.