How PHP 8 Attributes and Annotations Work

PHP 8 introduced attributes as a native, structured alternative to PHPDoc annotations. This article explains the core concepts of PHP 8 attributes, compares them with legacy docblock annotations, demonstrates how to define and apply them to your code, and shows how to retrieve their metadata using PHP’s native Reflection API.

What Are PHP 8 Attributes?

Attributes, often referred to as annotations in other programming languages, are a form of structured metadata that you can add to classes, methods, functions, properties, constants, and parameters. Before PHP 8, developers relied on unstructured PHPDoc comments (such as /** @Route("/api", methods={"GET"}) */) parsed at runtime by external libraries. PHP 8 replaces this workaround with a native, compiler-verified syntax.

The Syntax of PHP 8 Attributes

The syntax for attributes in PHP 8 uses the #[ and ] delimiters. You place the attribute directly above the declaration of the code block it describes.

#[MyAttribute]
class MyClass 
{
    #[MyAttribute('some_value')]
    public string $myProperty;
}

Attributes can accept arguments, including scalar values, arrays, class constants, and even nested attributes.

How to Create a Custom Attribute

To create an attribute, you define a standard PHP class and apply the native #[Attribute] attribute to it. This tells the PHP engine that the class can be used as metadata.

#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)]
class RoleRequired
{
    public function __construct(public string $role) {}
}

In the example above, the Attribute::TARGET_CLASS | Attribute::TARGET_METHOD flags restrict where this attribute can be applied. You can limit attributes to classes, methods, properties, parameters, or allow them globally.

How to Apply the Attribute

Once defined, you can apply your custom attribute to any compatible code element.

#[RoleRequired('admin')]
class AdminDashboardController
{
    #[RoleRequired('super_admin')]
    public function deleteUser()
    {
        // Controller logic
    }
}

How to Read Attributes Using Reflection

Because attributes are native to PHP, they do not require complex comment-parsing libraries. Instead, you read them at runtime using the PHP Reflection API.

The getAttributes() method is available on reflection objects, such as ReflectionClass, ReflectionMethod, and ReflectionProperty.

// Reflect the class
$reflection = new ReflectionClass(AdminDashboardController::class);

// Retrieve all 'RoleRequired' attributes on the class
$attributes = $reflection->getAttributes(RoleRequired::class);

foreach ($attributes as $attribute) {
    // Instantiate the attribute class to access its properties
    $roleRequiredInstance = $attribute->newInstance();
    
    echo $roleRequiredInstance->role; // Outputs: admin
}

Calling $attribute->newInstance() triggers the constructor of your custom attribute class, transforming the raw metadata into a fully typed PHP object.

Key Benefits of Native Attributes