Enums (PHP 8.1)
Esta aula apresenta as Enums introduzidas no PHP 8.1, cobrindo enums puras e com backing, métodos internos, casos de uso práticos e comparação com constantes tradicionais, com exemplos de código e exercícios.
As Enums (enumerações) foram introduzidas no PHP 8.1 como uma forma de representar um conjunto fixo de valores nomeados, trazendo segurança de tipo e expressividade ao código. Diferente de constantes ou classes com constantes, as enums são tipos por si só, permitindo que parâmetros, retornos e propriedades sejam tipados estritamente com o enum. Isso elimina erros comuns como passar valores inválidos ou misturar inteiros e strings semânticos.
Nesta aula, exploraremos os dois tipos de enums disponíveis: puras (sem valor associado) e com backing (com valor escalar), como adicionar métodos aos enums, casos de uso reais e por que elas são superiores a constantes soltas ou grupos de constantes.
Enums puros e com backing
Enums puros são aqueles que não possuem nenhum valor escalar associado a cada caso. Eles são úteis quando apenas a identidade do caso importa, como estados de uma máquina ou opções de configuração. Já os enums com backing permitem associar um valor inteiro ou string a cada caso, facilitando a integração com bancos de dados, APIs ou formulários.
Para definir um enum puro, usa-se a palavra-chave enum seguida do nome e dos casos separados por vírgula. Para enums com backing, adiciona-se : int ou : string após o nome e atribui-se um valor a cada caso. O PHP exige que todos os casos tenham um valor único e do mesmo tipo. Exemplos:
// Enum puro
enum StatusPedido {
case Pendente;
case Processando;
case Enviado;
case Entregue;
}
// Enum com backing (int)
enum StatusPagamento: int {
case Pendente = 0;
case Aprovado = 1;
case Recusado = 2;
case Estornado = 3;
}
// Enum com backing (string)
enum Categoria: string {
case Eletronicos = 'eletronicos';
case Roupas = 'roupas';
case Alimentos = 'alimentos';
}
Enums com backing podem ser convertidos para seu valor escalar usando a propriedade ->value e também podem ser criados a partir de um valor escalar com o método from() ou tryFrom() (que retorna null em vez de lançar exceção).
Métodos em enums
Assim como classes, enums podem ter métodos, incluindo construtores (apenas para enums com backing?), métodos estáticos e de instância. Isso permite encapsular lógica relacionada ao enum dentro dele mesmo, melhorando a coesão.
Exemplo de enum com método:
enum StatusPedido {
case Pendente;
case Processando;
case Enviado;
case Entregue;
public function descricao(): string {
return match($this) {
self::Pendente => 'Aguardando pagamento',
self::Processando => 'Preparando o pedido',
self::Enviado => 'Saiu para entrega',
self::Entregue => 'Recebido com sucesso',
};
}
}
echo StatusPedido::Enviado->descricao(); // 'Saiu para entrega'
Além disso, enums podem implementar interfaces, ter métodos estáticos e até constantes. Isso os torna tão poderosos quanto classes, mas com a restrição de que o número de casos é fixo.
Casos de uso
Enums são ideais para representar conjuntos finitos de valores que não mudam em tempo de execução, como:
- Estados de um processo (pedido, pagamento, usuário)
- Tipos de notificação (email, SMS, push)
- Opções de configuração (ordenação, filtros)
- Categorias fixas (gêneros, cores, tamanhos)
- Códigos de erro ou status HTTP
Exemplo prático: uma função que processa um pedido e atualiza seu status:
function processarPedido(Pedido $pedido): StatusPedido {
// lógica de processamento
if ($pedido->pago) {
return StatusPedido::Processando;
}
return StatusPedido::Pendente;
}
Com enums, o tipo de retorno é explícito e seguro, evitando strings mágicas ou inteiros soltos.
vs constantes
Antes das enums, usávamos constantes de classe (como class Status { const PENDENTE = 'pendente'; }) ou constantes globais define(). Embora funcionem, apresentam desvantagens:
- Falta de tipo específico: qualquer string ou inteiro pode ser passado, causando erros.
- Sem autocomplete: IDEs não sugerem valores válidos com tanta precisão.
- Sem métodos: constantes não podem ter comportamento associado.
- Sem validação: não há garantia de que o valor está entre os definidos.
Enums resolvem tudo isso: são tipos reais, permitem métodos, e o PHP verifica se o valor é válido em tempo de compilação (ou execução com from()). Além disso, enums podem ser usados em match com cobertura exaustiva, garantindo que todos os casos sejam tratados.
// Com constantes
function desconto(string $categoria): float {
switch($categoria) {
case 'eletronicos': return 0.1;
case 'roupas': return 0.2;
default: return 0;
}
}
// Com enum
function desconto(Categoria $categoria): float {
return match($categoria) {
Categoria::Eletronicos => 0.1,
Categoria::Roupas => 0.2,
default => 0,
};
}
O uso de enums torna o código mais expressivo, seguro e fácil de manter.
Boas práticas
- Prefira enums a constantes sempre que houver um conjunto fixo de valores.
- Use enums com backing quando precisar de interoperabilidade com dados externos.
- Implemente métodos para centralizar a lógica relacionada ao enum.
- Evite enums muito grandes (mais de 20 casos); considere se realmente são fixos.
- Utilize
tryFrom()para conversões seguras que podem falhar.
Referências
- PHP Manual: Enumerations
- PHP Manual: Backed Enums
- PHP RFC: Enumerations
- Stitcher.io: PHP Enums
- Dev.to: PHP 8.1 Enums
Exercícios
-
Crie um enum puro chamado
DiaSemanacom os dias da semana (segunda a domingo). Em seguida, crie uma função que receba umDiaSemanae retorne se é dia útil (segunda a sexta) ou fim de semana.✓ Resposta:enum DiaSemana { case Segunda; case Terca; case Quarta; case Quinta; case Sexta; case Sabado; case Domingo; } function tipoDia(DiaSemana $dia): string { return match($dia) { DiaSemana::Sabado, DiaSemana::Domingo => 'Fim de semana', default => 'Dia útil', }; } echo tipoDia(DiaSemana::Segunda); // Dia útil -
Defina um enum com backing string chamado
StatusTarefacom valores 'pendente', 'em_andamento', 'concluida' e 'cancelada'. Crie um método que retorne uma cor CSS associada (ex.: pendente -> amarelo, em_andamento -> azul, concluida -> verde, cancelada -> vermelho).✓ Resposta:enum StatusTarefa: string { case Pendente = 'pendente'; case EmAndamento = 'em_andamento'; case Concluida = 'concluida'; case Cancelada = 'cancelada'; public function cor(): string { return match($this) { self::Pendente => '#FFD700', self::EmAndamento => '#1E90FF', self::Concluida => '#32CD32', self::Cancelada => '#FF4500', }; } } echo StatusTarefa::EmAndamento->cor(); // #1E90FF -
Escreva um enum com backing int chamado
NivelAcessocom valores 1 (Admin), 2 (Editor), 3 (Usuário). Adicione um método estático que, dado um inteiro, retorne o caso correspondente usandotryFrom()ou null se inválido.✓ Resposta:enum NivelAcesso: int { case Admin = 1; case Editor = 2; case Usuario = 3; public static function deInt(int $valor): ?self { return self::tryFrom($valor); } } $nivel = NivelAcesso::deInt(2); // NivelAcesso::Editor $nivelInvalido = NivelAcesso::deInt(5); // null -
Crie um enum puro
OperacaoMatematicacom casos Soma, Subtracao, Multiplicacao e Divisao. Implemente um métodocalcular(int $a, int $b): int|floatque execute a operação correspondente.✓ Resposta:enum OperacaoMatematica { case Soma; case Subtracao; case Multiplicacao; case Divisao; public function calcular(int $a, int $b): int|float { return match($this) { self::Soma => $a + $b, self::Subtracao => $a - $b, self::Multiplicacao => $a * $b, self::Divisao => $a / $b, }; } } echo OperacaoMatematica::Soma->calcular(10, 5); // 15 -
Explique por que usar enums é melhor do que constantes de classe para representar os status de um pedido. Dê um exemplo de código comparando as duas abordagens.
✓ Resposta:
Enums oferecem segurança de tipo, autocomplete, métodos e validação em tempo de compilação/execução. Constantes são apenas valores escalares que podem ser misturados. Exemplo:
// Com constantes class Status { const PENDENTE = 0; const APROVADO = 1; } function processar(int $status) { /* aceita qualquer inteiro */ } // Com enum enum Status: int { case Pendente = 0; case Aprovado = 1; } function processar(Status $status) { /* só aceita Status */ }