O PHP 8.x trouxe as mudanças mais significativas da linguagem em uma década. Enums nativos eliminam a necessidade de constantes mágicas e classes de constantes. Fibers introduzem concorrência cooperativa no núcleo da linguagem. Readonly classes tornam objetos de valor imutáveis com uma única palavra-chave. Atributos nativos substituem comentários PHPDoc para metadados estruturados e verificáveis.
Esses recursos não são apenas conveniências sintáticas — eles mudam a forma como você modela domínios, escreve código concorrente e documenta comportamentos esperados. Frameworks modernos já os adotam intensamente: o Laravel usa Enums em cast de Eloquent, usa Atributos para rotas e injeção de dependência, e o PHP-DI usa Atributos para configurar containers.
Enums nativos — PHP 8.1
Antes do PHP 8.1, simular enumerações exigia classes com constantes, bibliotecas externas como myclabs/php-enum, ou simplesmente strings e inteiros soltos. Enums nativos resolvem isso com suporte do tipo system: um enum é um tipo, não apenas um valor.
PHP tem dois tipos de enum: Pure Enums (sem valor associado) e Backed Enums (associados a string ou int).
<?php
declare(strict_types=1);
// ── PURE ENUM — sem valor subjacente ─────────────────────────────────
enum Naipe
{
case Copas;
case Ouros;
case Espadas;
case Paus;
}
$carta = Naipe::Copas;
echo $carta->name . "\n"; // Copas
// Enums são tipos — type hints funcionam
function jogar(Naipe $naipe): string
{
return "Jogou: " . $naipe->name;
}
echo jogar(Naipe::Espadas) . "\n"; // Jogou: Espadas
// jogar("Copas") → TypeError — string não é Naipe
// ── BACKED ENUM — com valor string ───────────────────────────────────
enum Status: string
{
case Pendente = 'pendente';
case Aprovado = 'aprovado';
case Cancelado = 'cancelado';
case Entregue = 'entregue';
// Métodos em enums — lógica de domínio junto ao tipo
public function label(): string
{
return match($this) {
Status::Pendente => '⏳ Pendente',
Status::Aprovado => '✅ Aprovado',
Status::Cancelado => '❌ Cancelado',
Status::Entregue => '📦 Entregue',
};
}
public function podeSerCancelado(): bool
{
return in_array($this, [Status::Pendente, Status::Aprovado]);
}
}
// Backed Enums: from() lança ValueError, tryFrom() retorna null
$status = Status::from('aprovado'); // Status::Aprovado
$status = Status::tryFrom('invalido'); // null — não lança exceção
echo Status::Pendente->value . "\n"; // pendente
echo Status::Aprovado->label() . "\n"; // ✅ Aprovado
// Listar todos os casos
foreach (Status::cases() as $caso) {
echo $caso->value . " → " . $caso->label() . "\n";
}
// ── ENUM IMPLEMENTANDO INTERFACE ──────────────────────────────────────
interface TemLabel
{
public function label(): string;
}
enum Prioridade: int implements TemLabel
{
case Baixa = 1;
case Media = 2;
case Alta = 3;
case Critica = 4;
public function label(): string
{
return match($this) {
Prioridade::Baixa => '🟢 Baixa',
Prioridade::Media => '🟡 Média',
Prioridade::Alta => '🟠 Alta',
Prioridade::Critica => '🔴 Crítica',
};
}
// Constantes em enums
const PADRAO = self::Media;
}
echo Prioridade::Alta->label() . "\n"; // 🟠 Alta
echo Prioridade::PADRAO->label() . "\n"; // 🟡 Média
Readonly classes — PHP 8.2
PHP 8.1 introduziu readonly em propriedades individuais. PHP 8.2 tornou possível marcar a classe inteira como readonly — todas as propriedades tipadas se tornam readonly automaticamente. É o modelo perfeito para Value Objects e DTOs.
<?php
declare(strict_types=1);
// PHP 8.1 — readonly em propriedades individuais
class PontoV1
{
public function __construct(
public readonly float $x,
public readonly float $y,
) {}
}
// PHP 8.2 — readonly class — todas as propriedades são readonly automaticamente
readonly class Ponto
{
public function __construct(
public float $x,
public float $y,
) {}
// Readonly classes não podem ter propriedades não-readonly
// mas podem ter métodos normais
public function distanciaAte(Ponto $outro): float
{
return sqrt(($this->x - $outro->x) ** 2 + ($this->y - $outro->y) ** 2);
}
// Padrão wither — retorna nova instância com valor alterado (imutabilidade)
public function comX(float $x): static
{
return new static($x, $this->y);
}
public function comY(float $y): static
{
return new static($this->x, $y);
}
}
$a = new Ponto(0.0, 0.0);
$b = new Ponto(3.0, 4.0);
echo $a->distanciaAte($b) . "\n"; // 5
// $a->x = 1.0; → Error: Cannot modify readonly property
$c = $a->comX(1.0)->comY(2.0); // nova instância — $a intacto
echo $c->x . ", " . $c->y . "\n"; // 1, 2
// Value Objects como Readonly Classes
readonly class Dinheiro
{
public function __construct(
public int $centavos,
public string $moeda,
) {}
public function somar(Dinheiro $outro): static
{
if ($this->moeda !== $outro->moeda) {
throw new \InvalidArgumentException("Moedas diferentes: {$this->moeda} e {$outro->moeda}");
}
return new static($this->centavos + $outro->centavos, $this->moeda);
}
public function formatado(): string
{
return number_format($this->centavos / 100, 2, ',', '.') . " {$this->moeda}";
}
}
$preco = new Dinheiro(1999, 'BRL');
$frete = new Dinheiro(1500, 'BRL');
$total = $preco->somar($frete);
echo $total->formatado() . "\n"; // 34,99 BRL
Atributos nativos — PHP 8.0+
Atributos (antes chamados de Annotations em outras linguagens) permitem anexar metadados estruturados a classes, métodos, propriedades e parâmetros. Ao contrário dos comentários PHPDoc, atributos são verificados pelo PHP, podem receber parâmetros tipados, e são lidos via Reflection API com type safety.
<?php
declare(strict_types=1);
// Definindo um atributo — é uma classe com #[Attribute]
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)]
class Rota
{
public function __construct(
public readonly string $caminho,
public readonly string $metodo = 'GET',
) {}
}
#[Attribute(Attribute::TARGET_PROPERTY)]
class Validar
{
public function __construct(
public readonly string $tipo, // 'required', 'email', 'min', etc.
public readonly mixed $parametro = null,
) {}
}
#[Attribute(Attribute::TARGET_METHOD)]
class Cache
{
public function __construct(
public readonly int $ttlSegundos = 300,
public readonly string $prefixo = '',
) {}
}
// Usando os atributos
#[Rota('/api/usuarios')]
class UsuarioController
{
#[Rota('/api/usuarios/{id}', 'GET')]
#[Cache(ttlSegundos: 60, prefixo: 'usuario')]
public function mostrar(int $id): array
{
return ['id' => $id];
}
#[Rota('/api/usuarios', 'POST')]
public function criar(
#[Validar('required')]
#[Validar('email')]
string $email,
#[Validar('required')]
#[Validar('min', 8)]
string $senha,
): array {
return ['criado' => true];
}
}
// Lendo atributos via Reflection — como frameworks fazem
$reflection = new ReflectionClass(UsuarioController::class);
// Lê atributo da classe
$atributosClasse = $reflection->getAttributes(Rota::class);
foreach ($atributosClasse as $attr) {
$rota = $attr->newInstance();
echo "Classe: {$rota->caminho} [{$rota->metodo}]\n";
// Classe: /api/usuarios [GET]
}
// Lê atributos dos métodos
foreach ($reflection->getMethods() as $metodo) {
foreach ($metodo->getAttributes(Rota::class) as $attr) {
$rota = $attr->newInstance();
echo "Método {$metodo->getName()}: {$rota->caminho} [{$rota->metodo}]\n";
}
foreach ($metodo->getAttributes(Cache::class) as $attr) {
$cache = $attr->newInstance();
echo "Cache: TTL={$cache->ttlSegundos}s prefixo='{$cache->prefixo}'\n";
}
}
Fibers — PHP 8.1
Fibers são corrotinas de baixo nível — blocos de código que podem ser pausados e retomados, cedendo o controle ao chamador. Não são threads (não rodam em paralelo), mas permitem programação assíncrona cooperativa: múltiplas tarefas progridem intercaladas sem bloquear o processo.
Fibers são a base sobre a qual bibliotecas como ReactPHP e Amp implementam seus loops de eventos no PHP moderno.
<?php
declare(strict_types=1);
// Fiber básico — suspender e retomar
$fiber = new Fiber(function (): void {
echo "Fiber: início\n";
// Suspende e passa um valor para quem chamou resume()
$valor = Fiber::suspend('primeiro suspend');
echo "Fiber: retomado com '{$valor}'\n";
$valor2 = Fiber::suspend('segundo suspend');
echo "Fiber: retomado novamente com '{$valor2}'\n";
echo "Fiber: fim\n";
});
// start() executa até o primeiro Fiber::suspend()
$resultado = $fiber->start();
echo "Código principal recebeu: '{$resultado}'\n";
// resume() continua de onde parou, passando um valor para o Fiber
$resultado2 = $fiber->resume('ping');
echo "Código principal recebeu: '{$resultado2}'\n";
$fiber->resume('pong');
echo "Fiber terminado: " . ($fiber->isTerminated() ? "sim" : "não") . "\n";
// Output:
// Fiber: início
// Código principal recebeu: 'primeiro suspend'
// Fiber: retomado com 'ping'
// Código principal recebeu: 'segundo suspend'
// Fiber: retomado novamente com 'pong'
// Fiber: fim
// Fiber terminado: sim
// ── SCHEDULER SIMPLES — demonstrando concorrência cooperativa ──────────
class Scheduler
{
/** @var Fiber[] */
private array $fibers = [];
public function adicionar(Fiber $fiber): void
{
$this->fibers[] = $fiber;
}
public function executar(): void
{
// Inicia todos os fibers
foreach ($this->fibers as $fiber) {
$fiber->start();
}
// Continua rodando até todos terminarem
while (!empty($this->fibers)) {
foreach ($this->fibers as $i => $fiber) {
if ($fiber->isSuspended()) {
$fiber->resume();
}
if ($fiber->isTerminated()) {
unset($this->fibers[$i]);
}
}
}
}
}
$scheduler = new Scheduler();
$scheduler->adicionar(new Fiber(function (): void {
for ($i = 1; $i <= 3; $i++) {
echo "Tarefa A — passo {$i}\n";
Fiber::suspend(); // cede o controle
}
}));
$scheduler->adicionar(new Fiber(function (): void {
for ($i = 1; $i <= 3; $i++) {
echo "Tarefa B — passo {$i}\n";
Fiber::suspend(); // cede o controle
}
}));
$scheduler->executar();
// Tarefa A — passo 1
// Tarefa B — passo 1
// Tarefa A — passo 2
// Tarefa B — passo 2
// Tarefa A — passo 3
// Tarefa B — passo 3
Outros recursos PHP 8.x notáveis
<?php
declare(strict_types=1);
// ── NAMED ARGUMENTS — PHP 8.0 ─────────────────────────────────────────
// Argumentos nomeados — ordem não importa, leitura mais clara
$resultado = array_slice(array: [1,2,3,4,5], offset: 1, length: 3);
// [2, 3, 4]
// Especialmente útil com construtores complexos
$data = new \DateTimeImmutable(
datetime: '2024-01-15',
timezone: new \DateTimeZone('America/Sao_Paulo'),
);
// ── MATCH EXPRESSION — PHP 8.0 ────────────────────────────────────────
// match é uma expressão (tem retorno), comparação estrita, sem fallthrough
$codigo = 200;
$descricao = match($codigo) {
200, 201 => 'Sucesso',
400 => 'Requisição inválida',
401, 403 => 'Não autorizado',
404 => 'Não encontrado',
500 => 'Erro interno',
default => 'Código desconhecido',
};
// ── NULLSAFE OPERATOR — PHP 8.0 ───────────────────────────────────────
// Encadeia chamadas sem verificar null em cada passo
$cidade = $usuario?->endereco?->cidade?->nome;
// Equivale a: $usuario && $usuario->endereco && ... — mas conciso
// ── FIRST CLASS CALLABLES — PHP 8.1 ──────────────────────────────────
// Cria Closure a partir de qualquer callable com sintaxe limpa
$fn = strlen(...); // Closure de função nativa
echo $fn("olá") . "\n"; // 3
$dobrar = fn(int $n): int => $n * 2;
$numeros = [1, 2, 3, 4, 5];
$dobrados = array_map($dobrar, $numeros); // [2, 4, 6, 8, 10]
// ── INTERSECTION TYPES — PHP 8.1 ──────────────────────────────────────
// Já vimos no artigo anterior — objetos que satisfazem múltiplas interfaces
// ── NEVER RETURN TYPE — PHP 8.1 ───────────────────────────────────────
// Declara que a função NUNCA retorna — sempre lança exceção ou termina o processo
function falharSempre(string $mensagem): never
{
throw new \RuntimeException($mensagem);
}
// ── DNF TYPES — PHP 8.2 ───────────────────────────────────────────────
// Disjunctive Normal Form — combinação de union e intersection types
interface Stringable { public function __toString(): string; }
interface Countable { public function count(): int; }
interface JsonSerializable { public function jsonSerialize(): mixed; }
// Aceita (Stringable & Countable) OU JsonSerializable
function processar((Stringable&Countable)|JsonSerializable $dado): void
{
// ...
}
Resumo do artigo
| Recurso | Versão | O que resolve |
|---|---|---|
| Enums | PHP 8.1 | Tipos enumerados com type safety — elimina constantes mágicas |
| Backed Enums | PHP 8.1 | Enum com valor string/int — serialização e persistência |
| Readonly properties | PHP 8.1 | Imutabilidade de propriedades individuais |
| Readonly classes | PHP 8.2 | Imutabilidade de toda a classe — ideal para Value Objects |
| Atributos nativos | PHP 8.0 | Metadados estruturados verificáveis — substituem PHPDoc |
| Fibers | PHP 8.1 | Corrotinas cooperativas — base para programação assíncrona |
| Named arguments | PHP 8.0 | Argumentos por nome — legibilidade e flexibilidade |
| Match expression | PHP 8.0 | Switch tipado com retorno, sem fallthrough |
| Nullsafe operator | PHP 8.0 | Encadeamento seguro em valores nullable |
| Never return type | PHP 8.1 | Declara funções que nunca retornam |
| DNF Types | PHP 8.2 | Combinação de union e intersection types |
Exercício da semana
-
Modele um sistema de pedidos usando Enums:
StatusPedido(backed string com métodoslabel(),podeTransicionarPara(StatusPedido $novo): bool),MetodoPagamento(backed string: Pix, Cartao, Boleto) eTipoPrioridade(backed int: 1-3). Garanta que transições inválidas sejam bloqueadas. -
Crie uma
readonly class Enderecocom camposlogradouro,numero,complemento(nullable),cidade,estado,cep. Implemente métodosformatado(): stringecomComplemento(string $c): static. Crie tambémreadonly class Clienteque compõe umEndereco. -
Defina três atributos:
#[ValidarEmail],#[ValidarMinimo(int $min)]e#[ValidarMaximo(int $max)]. Crie uma funçãovalidarObjeto(object $obj): arrayque usa Reflection para ler os atributos das propriedades e retorna os erros encontrados. -
Escreva um mini-scheduler de Fibers que executa três "tarefas" em paralelo cooperativo: download simulado (5 passos), processamento (3 passos) e log (4 passos). Cada passo imprime o nome da tarefa e suspende. O scheduler intercala a execução.
-
Desafio: combine os recursos do artigo para criar um micro-framework de rotas: atributo
#[Rota(string $path, string $metodo)], umRouterque usa Reflection para descobrir controllers e métodos anotados, e mapeia requisições{metodo} {path}para o método correto. Use um EnumMetodoHttppara os verbos.
Referências
- PHP Manual — Enumerations: https://www.php.net/manual/pt_BR/language.enumerations.php
- PHP Manual — Readonly Properties (8.1): https://www.php.net/manual/pt_BR/language.oop5.properties.php#language.oop5.properties.readonly-properties
- PHP Manual — Readonly Classes (8.2): https://www.php.net/manual/pt_BR/language.oop5.basic.php#language.oop5.basic.class.readonly
- PHP Manual — Attributes: https://www.php.net/manual/pt_BR/language.attributes.php
- PHP Manual — Fibers: https://www.php.net/manual/pt_BR/language.fibers.php
- PHP 8.0 Release Notes: https://www.php.net/releases/8.0/pt_BR.php
- PHP 8.1 Release Notes: https://www.php.net/releases/8.1/pt_BR.php
- PHP 8.2 Release Notes: https://www.php.net/releases/8.2/pt_BR.php