What Are PHP Attributes?

PHP 8.0 introduced Attributes (sometimes called Annotations) as a first-class, native language feature. An Attribute is a structured, machine-readable piece of metadata you can attach to classes, methods, properties, parameters, constants, or functions using the #[...] syntax.

Before Attributes, frameworks like Laravel relied on docblock comments parsed at runtime, or required you to implement specific interfaces or define properties/methods in your classes. Attributes replace all of that with something the PHP engine understands natively — no reflection hacks required.

Basic Anatomy of a PHP Attribute

// 1. Define an Attribute class
#[Attribute]
class MyRoute
{
    public function __construct(
        public readonly string $path,
        public readonly string $method = 'GET',
    ) {}
}

// 2. Apply it to a class or method
#[MyRoute(path: '/hello', method: 'GET')]
function helloWorld(): string
{
    return 'Hello, World!';
}

// 3. Read it at runtime with Reflection
$rf   = new ReflectionFunction('helloWorld');
$attr = $rf->getAttributes(MyRoute::class)[0];
$inst = $attr->newInstance(); // → MyRoute object
echo $inst->path;            // → /hello

 

Key facts to know:

  • Attributes are zero-cost when not inspected - PHP does not instantiate them unless you call ->newInstance() via reflection.
  • An Attribute class is just a regular PHP class decorated with #[Attribute].
  • You can restrict where an Attribute is allowed (class only, method only,  etc.). Using the Attribute::TARGET_* flags.
  • Multiple Attributes can be stacked on the same target: #[Attr1] #[Attr2]. 

Laravel's strategy: Laravel reads your Attributes during the service-container boot phase or request lifecycle — so you write zero runtime code; the framework does the heavy lifting with Reflection under the hood.

Why Laravel is going All-In On Attributes

Laravel's design philosophy has always been about expressiveness and reduced boilerplate. Attributes fit perfectly because they let you declare intent right next to the code that implements it, instead of spreading configuration across routes files, service providers, and constructors.

Compare the "old way" vs. the "Attribute way" for middleware on a controller:

Before Attributes (Laravel 10/11 style)

class CommentController extends Controller
{
    public function __construct()
    {
        // Had to define middleware in the constructor
        $this->middleware('auth');
        $this->middleware('subscribed')->only('store');
    }
}

 

After Attributes (Laravel 13 style)

#[Middleware('auth')]
class CommentController
{
    #[Middleware('subscribed')]
    public function store(Post $post): Response
    {
        // Intent is immediately obvious here
    }
}

The Attribute version is self-documenting — a developer reading the method immediately knows what middleware and policies apply, without jumping to a constructor or routes file.


 

Every Laravel 13 Attribute — Documented

Below is the complete, categorised reference. For each Attribute you'll find a description, code example, use case, and a link to the official Laravel 13 documentation page.

Attributes at a Glance

# ATTRIBUTE AREA REPLACES
1 #[Table] Eloquent $table, $primaryKey, $timestamps...
2 #[Fillable] Eloquent $fillable
3 #[Guarded] Eloquent $guarded
4 #[Unguarded] Eloquent Model::unguard() call
5 #[Hiddem] Eloquent $hidden
6 #[Visible] Eloquent $visible
7 #[Appends] Eloquent $appends
8 #[Casts] Eloquent $casts / casts()