Os Atributos (ou Attributes) chegaram ao PHP com a versão 8.0 e marcaram uma das mudanças mais importantes da linguagem.
Eles permitem adicionar metadados diretamente ao código, de forma nativa, segura e legível — sem precisar de docblocks ou arquivos externos.
Em outras palavras, Atributos são anotações interpretadas pelo próprio PHP, que permitem construir sistemas mais declarativos, automáticos e enxutos.
Neste artigo, você vai aprender:
- O que são Atributos no PHP
- Como criar e ler Atributos
- Exemplos reais de uso (rotas, validações, ORM)
- Boas práticas para aplicá-los em projetos modernos
Atributos no PHP: visão geral
Atributos são metadados nativos que descrevem comportamentos de classes, métodos, propriedades ou funções.
Eles são processados em tempo de execução, via Reflection API.
Antes do PHP 8, as anotações eram feitas por meio de comentários, desta forma:
/**
* @Route("/users", methods={"GET"})
*/
public function index()
{
// implementação do método `index`
}
Esses comentários não eram reconhecidos pelo PHP — dependiam de ferramentas externas como Doctrine, PHPUnit, ou frameworks.
Com Atributos, isso se torna parte real da linguagem:
#[Route(path: '/users', methods: ['GET'])]
public function index()
{
// implementação do método `index`
}
Dessa forma é mais seguro, pois estamos referenciando classes, que serão processadas via Reflexion API. Ou seja, a interpretação não depende mais de processar texto em comentários, como se fazia com as anotações, usando arroba (@).
Como criar um Atributo
Um Attribute é simplesmente uma classe PHP com a anotação #[Attribute].
Vamos a um exemplo básico:
<?php
use Attribute;
#[Attribute]
class Route {
public function __construct(
public string $path,
public array $methods = ['GET']
) { }
}
Agora podemos usá-lo em qualquer método:
class UserController
{
#[Route(path: '/users', methods: ['GET'])]
public function index()
{
// lógica da rota
}
}
TARGET: Onde o atributo pode ser usado
Todo Atributo pode (e deveria) declarar onde ele pode ser aplicado. Dessa forma, cada atributo só pode ser aplicado aos elementos onde ele foi definido como válido para ser usado.
Por exemplo, podemos restringir atributos para serem usados apenas em classes, apenas em métodos, propriedades etc.
Isso é feito através das seguintes constantes:
Attribute::TARGET_CLASS
Attribute::TARGET_METHOD
Attribute::TARGET_FUNCTION
Attribute::TARGET_PROPERTY
Attribute::TARGET_PARAMETER
Attribute::TARGET_CLASS_CONSTANT
Attribute::TARGET_ALL
Para criarmos um atributo que só pode ser utilizado em classes, podemos fazer isto:
use Attribute;
#[Attribute(Attribute::TARGET_CLASS)]
class Route {
public function __construct(public string $path) {}
}
Isso evita que o atributo seja utilizado em métodos, propriedades, ou qualquer outro elemento que não seja uma classe.
Se ele for utilizado em algo que não seja uma classe, receberemos este erro do PHP:
Erro: Attribute can only be applied to classes
Também é possível definir múltiplos TARGETs, permitindo o uso do atributo em vários elementos. Por exemplo:
#[Attribute(
Attribute::TARGET_CLASS |
Attribute::TARGET_METHOD |
Attribute::TARGET_FUNCTION
)]
class Log {}
Atributos repetíveis (repeatable Attributes)
Por padrão, um Attribute não pode ser aplicado mais de uma vez no mesmo elemento. Isso pode ser um problema caso seu atributo precise ser aplicado mais de uma vez a um mesmo elemento, com parâmetros diferentes.
Para criar um atributo repetível (repeatable), use a constante Attribute::IS_REPEATABLE, como no exemplo a seguir.
#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
class Middleware {
public function __construct(public string $name) {}
}
Com isso, podemos definir mais de um middleware a um método, desta forma:
#[Middleware('auth')]
#[Middleware('admin')]
function dashboard() {}
Como ler Atributos usando Reflexion
A grande vantagem dos Atributos é que eles podem ser lidos e processados dinamicamente pelo próprio PHP, usando a Reflexion API.
Vamos a um exemplo:
$reflection = new ReflectionMethod(UserController::class, 'index');
$attributes = $reflection->getAttributes(Route::class);
foreach ($attributes as $attribute) {
$instance = $attribute->newInstance();
echo $instance->path; // /users
print_r($instance->methods); // ['GET']
}
Essa estrutura permite construir roteadores automáticos, injeção de dependências, ou até validações dinâmicas.
Herança e Attributes
Por padrão, Atributos não são herdados ao estender classes.
Traduzindo em código PHP:
#[Entity]
class BaseModel {}
// aqui, a classe User não possui o atributo Entity
class User extends BaseModel {}
Se executarmos o código abaixo na classe User, receberemos um array vazio.
(new ReflectionClass(User::class))->getAttributes()
Se você quiser herdar o comportamento, precisa implementar manualmente:
$parent = $ref->getParentClass();
if ($parent) {
$inherited = $parent->getAttributes(Entity::class);
}
Atributos na prática: validador automático
Vamos criar um sistema de validação automática. Para isso, primeiro criaremos um Atributo chamado Required, desta forma:
use Attribute;
#[Attribute(Attribute::TARGET_PROPERTY)]
class Required
{
//
}
Esse atributo representará um valor obrigatório na validação.
Agora vamos aplicar esse atributo a uma classe que representa uma requisição HTTP:
class CreateUserRequest
{
#[Required]
public string $name;
#[Required]
public string $email;
}
E vamos criar uma função que faz a validação:
function validate(object $request): void
{
$reflection = new ReflectionObject($request);
foreach ($reflection->getProperties() as $property) {
$attributes = $property->getAttributes(Required::class);
if (!empty($attributes) && empty($property->getValue($request))) {
throw new Exception("O campo '{$property->getName()}' é obrigatório.");
}
}
}
E para utilizar a função:
$request = new CreateUserRequest();
$request->name = 'John Doe';
$request->email = ''; // vazio
validate($request); // Erro: campo 'email' é obrigatório
Atributos no mundo real: definindo rotas
O uso de Attributes é comum em frameworks inspirados em Laravel, Symfony e Slim.
#[Attribute(Attribute::TARGET_METHOD)]
class Route
{
public function __construct(
public string $path,
public string $method
) {}
}
class PostController
{
#[Route('/posts', 'GET')]
public function index()
{
// ...
}
#[Route('/posts', 'POST')]
public function store()
{
// ...
}
}
O arquivo responsável pelo roteamento pode buscar automaticamente todas as rotas desta forma:
$controller = new ReflectionClass(PostController::class);
foreach ($controller->getMethods() as $method) {
foreach ($method->getAttributes(Route::class) as $attr) {
$route = $attr->newInstance();
echo "{$route->method} {$route->path}\n";
}
}
Como saída, teremos:
GET /posts
POST /posts
Ou seja, sem arquivos de configuração, sem arquivos de rotas. Tudo declarado direto no código — fácil de manter e testar.
Atributos para mapear entidades em ORM
Frameworks como Doctrine ORM já adotaram Attributes para mapear entidades. Veja um exemplo:
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
#[ORM\Table(name: "users")]
class User
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: "integer")]
public int $id;
#[ORM\Column(length: 100)]
public string $name;
}
Esses metadados permitem que o ORM entenda automaticamente o modelo, sem precisar de XML ou docblocks.
Boas práticas ao usar Atributos
- Use nomes claros:
#[Route],#[Required],#[Entity] - Evite lógica de negócio dentro de Attributes
- Limite seu uso a metadados e configuração
- Combine com Reflection ou frameworks
- Documente bem o propósito de cada Atributo
Quando usar Atributos no PHP
Use Atributos quando quiser:
- Declarar regras e comportamentos no próprio código
- Eliminar arquivos externos de configuração
- Criar APIs auto-documentadas
- Automatizar testes, validações e rotas
Eles são ideais para projetos com arquitetura modular ou frameworks personalizados.
Vantagens dos Atributos
- Código mais limpo e declarativo
- Menos dependência de parsing de docblocks
- Desempenho melhor (sem reflexão de comentários)
- Mais integração com IDEs e ferramentas de análise estática
Os Atributos tornaram o desenvolvimento moderno mais elegante, limpo e expressivo.
Eles eliminam arquivos de configuração complexos e permitem que o código explique a si mesmo.
Hoje, Attributes são base de frameworks modernos, ferramentas de teste, roteamento e validação.
Se você ainda não os usa, este é o momento de começar.

Sou Roberto Beraldo (ou apenas Beraldo), desenvolvedor PHP há mais de 15 anos, numa jornada de desenvolvimento para me aprofundar no mundo DevOps e Cloud. Bacharel em Ciência da Computação, com uma base sólida em desenvolvimento web e conhecimentos em DevOps e Computação em Nuvem. Estou dedicado a conectar o desenvolvimento de software com a gestão de infraestrutura.