Attributes
Attributes (PHP 8.0+) provide a way to add metadata to classes, methods, properties, and functions without modifying the code itself.
Basic Syntax
Last updated: April 6, 2026 Minimum PHP Version: PHP 8.0+ Status: Stable
PHP Version Support
| Feature | PHP 7.4 | PHP 8.0+ | PHP 8.1+ | PHP 8.2+ |
|---|---|---|---|---|
| Attributes | ❌ | ✅ | ✅ | ✅ |
| Class Attributes | ❌ | ✅ | ✅ | ✅ |
| Method Attributes | ❌ | ✅ | ✅ | ✅ |
| Property Attributes | ❌ | ✅ | ✅ | ✅ |
| Function Attributes | ❌ | ✅ | ✅ | ✅ |
| Parameter Attributes | ❌ | ❌ | ❌ | ✅ |
<?php
#[Attribute]
class MyAttribute
{
public function __construct(
public string $description = ''
) {}
}
?>
Using Attributes
<?php
#[Attribute]
class Route
{
public function __construct(
public string $path,
public string $method = 'GET'
) {}
}
#[Route('/users', method: 'GET')]
class UserController
{
#[Route('/users', method: 'POST')]
public function create(): void
{
// Create user
}
#[Route('/users/{id}', method: 'GET')]
public function show(int $id): void
{
// Show user
}
}
?>
Reading Attributes
<?php
#[Attribute]
class Route
{
public function __construct(
public string $path,
public string $method = 'GET'
) {}
}
#[Route('/api/users')]
class UserController
{
#[Route('/api/users/{id}', method: 'GET')]
public function getUser(int $id): void {}
}
$reflection = new ReflectionClass(UserController::class);
$attributes = $reflection->getAttributes(Route::class);
foreach ($attributes as $attribute) {
$route = $attribute->newInstance();
echo $route->path; // /api/users
}
?>
Common Use Cases
Validation
<?php
#[Attribute]
class NotBlank
{
public function __construct(
public string $message = 'This field cannot be blank'
) {}
}
#[Attribute]
class MinLength
{
public function __construct(
public int $length,
public string $message = ''
) {}
}
class UserDTO
{
public function __construct(
#[NotBlank]
#[MinLength(3)]
public string $username,
#[NotBlank]
#[MinLength(8)]
public string $password
) {}
}
?>
Caching
<?php
#[Attribute]
class Cacheable
{
public function __construct(
public int $ttl = 3600,
public string $key = ''
) {}
}
class ProductService
{
#[Cacheable(ttl: 600, key: 'product_list')]
public function getProducts(): array
{
// Expensive database query
return [];
}
}
?>
Deprecation
<?php
#[Deprecated(
replacement: 'newMethod',
since: '2.0'
)]
function oldMethod(): void
{
// Old implementation
}
?>
Built-in PHP Attributes
#[ReturnTypeWillChange]
<?php
class MyArray implements ArrayAccess
{
#[ReturnTypeWillChange]
public function offsetGet($key) { }
#[ReturnTypeWillChange]
public function offsetSet($key, $value) { }
#[ReturnTypeWillChange]
public function offsetExists($key): bool { }
#[ReturnTypeWillChange]
public function offsetUnset($key) { }
}
?>
#[AllowDynamicProperties]
<?php
class StdClass
{
#[AllowDynamicProperties]
public string $dynamic = 'value';
}
?>
#[NoDiscard]
<?php
#[NoDiscard]
function getConfig(): Config
{
return new Config();
}
getConfig(); // Warning: discarded return value
?>
Attribute vs PHPDoc Annotations
| Feature | Attributes | PHPDoc |
|---|---|---|
| Syntax | Native PHP syntax | Docblock comments |
| Parseable at runtime | Yes | Requires reflection |
| IDE support | Native | Via plugins |
| Type checking | Yes | No |
| Performance | No overhead | Parsing required |
| Tooling support | Growing | Extensive |