Generics em PHP: Tipagem que faltava para você

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: Por que desenvolvedores PHP sonham com genéricos

\n\n

Se você já escreveu em Java, C# ou TypeScript e depois voltou ao PHP, certamente sentiu uma leve decepção. Nessas linguagens, existe um sistema elegante de tipos genéricos (Generics), que permite criar estruturas de dados flexíveis, mas seguras do ponto de vista de tipos. No PHP, por muito tempo reinou a "anarquia de tipagem": arrays podiam conter qualquer coisa, e a IDE só destacava os erros mais óbvios.

\n\n

Mas os tempos estão mudando. A partir do PHP 7.0 e especialmente na era do PHP 8.0/8.1/8.2, a comunidade encontrou uma maneira de usar Generics em PHP. Sim, no nível da sintaxe da linguagem eles ainda não existem (e provavelmente não existirão nos próximos anos), mas isso não significa que você não possa aproveitar todos os benefícios da programação genérica hoje.

\n\n

Neste artigo, vamos explorar como funciona a tipagem em PHP com Generics, quais ferramentas existem para isso (PHPStan, Psalm) e como escrever código que seja ao mesmo tempo flexível e seguro. Aperte os cintos — vai esquentar!

\n\n

O que são Generics e por que são necessários no PHP?

\n\n

Generics (tipos genéricos) são um mecanismo que permite parametrizar classes, interfaces e funções com tipos. Em vez de escrever uma implementação separada para cada tipo de dado, você cria uma universal e insere o tipo específico no momento do uso.

\n\n

Imagine que você está escrevendo uma classe para fila (Queue). Sem genéricos, você pode armazenar apenas um tipo de dado ou usar mixed (o que destrói toda a tipagem). Com Generics, você pode dizer: "Esta é uma fila de números inteiros" ou "Esta é uma fila de objetos User".

\n\n

O problema da tipagem "crua"

\n\n

Veja este código PHP típico:

\n\n
<?php\n\nclass Collection {\n    private array $items = [];\n\n    public function add($item): void {\n        $this->items[] = $item;\n    }\n\n    public function get(int $index) {\n        return $this->items[$index] ?? null;\n    }\n}\n\n$users = new Collection();\n$users->add(new User('Alice'));\n$users->add(new User('Bob'));\n$users->add('isto é uma string, não um usuário'); // Erro! Mas PHP fica em silêncio\n\n$user = $users->get(2); // Retorna uma string, a IDE não suspeitará de nada\n$user->getName(); // Erro fatal em tempo de execução\n
\n\n

Este é o problema clássico do desenvolvedor PHP: falta de controle de tipos na fase de escrita do código. Você só descobre o erro em tempo de execução, quando o usuário já clicou no botão. Generics resolvem esse problema pela raiz.

\n\n

Generics em PHP: Como funciona na prática

\n\n

Como a sintaxe nativa de Generics no PHP está ausente, usamos anotações no PHPDoc, que são processadas por analisadores estáticos — PHPStan e Psalm. Essas ferramentas verificam o código quanto à conformidade com os tipos declarados, emulando efetivamente o funcionamento dos genéricos.

\n\n

Sintaxe básica: tipos de template

\n\n

Suponha que queremos tornar nossa coleção type-safe. Veja como fica usando Generics via PHPDoc:

\n\n
<?php\n\n/**\n * @template T\n */\nclass GenericCollection {\n    /** @var array<int, T> */\n    private array $items = [];\n\n    /**\n     * @param T $item\n     */\n    public function add($item): void {\n        $this->items[] = $item;\n    }\n\n    /**\n     * @param int $index\n     * @return T|null\n     */\n    public function get(int $index) {\n        return $this->items[$index] ?? null;\n    }\n}\n\n// Agora usamos:\n/** @var GenericCollection<User> $users */\n$users = new GenericCollection();\n$users->add(new User('Alice'));\n$users->add('string'); // PHPStan emitirá erro: Expected type User, got string\n\n$user = $users->get(0); // PHPStan sabe que é User\necho $user->getName(); //                

Blogs

Book Recommendations