Introdução: Por que desenvolvedores PHP sonham com genéricos
\n\nSe 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\nMas 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\nNeste 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\nO que são Generics e por que são necessários no PHP?
\n\nGenerics (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\nImagine 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".
O problema da tipagem "crua"
\n\nVeja 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\nEste é 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\nGenerics em PHP: Como funciona na prática
\n\nComo 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\nSintaxe básica: tipos de template
\n\nSuponha 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(); //