Attribute là metadata có cấu trúc gắn vào class, method, property, tham số hoặc hằng số, viết bằng cú pháp #[...].
Trước PHP 8, metadata phải nhét vào docblock rồi parse chuỗi bằng thư viện ngoài (Doctrine Annotations).
#[Attribute(Attribute::TARGET_METHOD)]
final class Route
{
public function __construct(
public readonly string $path,
public readonly string $method = 'GET',
) {}
}
class OrderController
{
#[Route('/orders/{id}', method: 'GET')]
public function show(int $id) { /* ... */ }
}Đọc lại bằng Reflection:
$ref = new ReflectionMethod(OrderController::class, 'show');
foreach ($ref->getAttributes(Route::class) as $attr) {
$route = $attr->newInstance(); // object Route thật, đã kiểm kiểu
}Khác biệt cốt lõi so với docblock annotation:
- Attribute được engine phân tích cú pháp; sai tên class hay sai tham số là lỗi ngay, còn docblock chỉ là comment nên gõ sai vẫn im lặng.
- newInstance() trả về object thật, có type check, IDE gợi ý được.
- Không bị mất khi bật opcache.save_comments=0 — docblock thì có.
- Attribute không tự chạy; nó chỉ là dữ liệu cho tới khi có code dùng Reflection đọc nó.
Laravel dùng attribute ở nhiều chỗ: #[ObservedBy(OrderObserver::class)], #[ScopedBy(...)] trên model, #[Group] / #[Test] trong PHPUnit 11, #[Deprecated] sẵn có của PHP. Chi phí Reflection có thật, nên framework thường cache kết quả quét attribute thay vì đọc lại mỗi request.