Toda vez que aparece uma thread sobre "PHP morreu", eu abro o terminal de algum projeto em produção e olho o que está rodando ali: Laravel 12, PHP 8.4, filas em Redis, painel em Filament, testes em Pest, deploy em Docker atrás de Nginx. Nada disso lembra o PHP que virou meme. E, sinceramente, a defesa da linguagem já foi ganha faz tempo — o problema hoje é outro.
O problema é que muito time escreve código de 2013 dentro de um framework de 2026. A stack evoluiu; o hábito, não.
O sintoma: array como contrato
O primeiro cheiro é sempre o mesmo. Um método de service que recebe array $data, mexe em dez chaves e devolve outro array. Ninguém sabe o que entra, ninguém sabe o que sai, e o autocomplete da IDE vira decoração. Aí o time compensa isso com documentação em Notion, que envelhece em duas sprints.
PHP tem tipagem forte o suficiente para acabar com isso desde a 7.4, e desde a 8.x ficou confortável de verdade. Um objeto de valor com readonly resolve mais do que um bloco de PHPDoc:
final readonly class NovaAssinatura
{
public function __construct(
public int $clienteId,
public PlanoAssinatura $plano,
public Carbon $inicioEm,
public ?string $cupom = null,
) {}
}
Agora o contrato existe no código, não na cabeça de quem escreveu. Se alguém tentar passar um cupom como int, o erro aparece no CI, não no suporte.
Enum é a ferramenta mais subaproveitada da linguagem
Máquina de estados em string solta é a causa de bug mais chata que já depurei. 'pending', 'PENDING', 'pendente', 'aguardando' — todos no mesmo banco, todos escritos por pessoas diferentes ao longo de dois anos. Enums nativos, que chegaram no PHP 8.1, matam essa categoria inteira de problema.
enum StatusDemanda: string
{
case Aberta = 'aberta';
case EmVisita = 'em_visita';
case Orcada = 'orcada';
case Concluida = 'concluida';
public function podeAvancarPara(self $proximo): bool
{
return in_array($proximo, $this->proximosPassos(), strict: true);
}
}
O Eloquent faz o cast direto no model, o Filament monta o select sozinho a partir do enum, e a regra de transição fica no mesmo arquivo que define os estados. Num marketplace de serviços que eu mantenho, esse único movimento cortou a maior fonte de chamado do suporte: pedido que "pulava" etapa porque alguém escreveu a string errada numa migration de correção.
Fila não é otimização, é design
A outra herança do PHP antigo é achar que tudo tem que acontecer dentro do request. Envio de e-mail, geração de PDF, chamada de gateway, processamento de imagem — tudo empurrado no ciclo do HTTP porque "é rápido". Até o dia em que o gateway demora oito segundos e o cliente clica duas vezes no botão de pagar.
Filas mudam a forma de modelar a operação, não só o tempo de resposta. Quando o job é a unidade de trabalho, você ganha de graça três coisas que dificilmente ia implementar na mão: retentativa com backoff, isolamento de falha e visibilidade. Eu tenho pipeline de vídeo em produção onde cada etapa — baixar, transcrever, selecionar trecho, cortar, publicar — é um job separado. Quando a transcrição falha por limite de API, só aquela etapa volta para a fila. O resto do pipeline não sabe e não precisa saber.
A regra prática que uso: se a operação depende de um sistema que eu não controlo, ela não roda no request.
Teste que você escreve porque quer
A objeção clássica contra teste em PHP era a ergonomia. PHPUnit funciona, mas escrever suíte nele nunca foi prazeroso. Pest resolveu isso de um jeito quase injusto — a barreira caiu tanto que hoje é mais rápido escrever o teste do que abrir o Postman e reproduzir o cenário na mão.
it('bloqueia checkout quando a loja está fechada', function () {
$loja = Loja::factory()->fechada()->create();
postJson('/api/checkout', payloadValido($loja))
->assertStatus(422)
->assertJsonPath('erro', 'loja_fechada');
});
Não é sobre cobertura de 100%. É sobre ter uma rede embaixo das regras que dão dinheiro: pagamento, cupom, zona de entrega, transição de status. Essas eu testo sempre. Layout de e-mail, não.
O que eu não faço mais
Duas coisas que já defendi e abandonei.
A primeira é encher o projeto de camada. Repository que só faz proxy pro Eloquent, DTO para toda ação, interface com uma implementação só. Isso não é arquitetura limpa, é imposto de indireção. O Eloquent já é a camada de dados; brigar com ele custa caro e não devolve nada.
A segunda é perseguir a última versão de tudo no dia do lançamento. Laravel tem ciclo previsível e política de suporte clara justamente para você planejar a atualização. Subir versão major numa sexta porque saiu no Twitter é escolha, não obrigação.
O ponto
PHP moderno é uma linguagem tipada, com enums, com atributos, com um ecossistema que resolve fila, painel, teste e deploy sem você escrever nada disso do zero. A linguagem cumpriu a parte dela. Falta o hábito acompanhar — e isso não se resolve trocando de stack, se resolve lendo a documentação da versão que você está usando e parando de escrever array $data.