Generics em PHP: para que servem e como usar
O desenvolvimento moderno em PHP exige não apenas velocidade, mas também confiabilidade do código. Uma das ferramentas que ajuda a atingir esse objetivo são os Generics (tipos genéricos). Apesar de o PHP ser uma linguagem de tipagem dinâmica, com o lançamento do PHP 7.0 e superiores, e especialmente com o PHP 8.0, a tipagem estática tornou-se disponível e demandada. Os Generics permitem escrever estruturas de dados mais flexíveis, reutilizáveis e seguras, como coleções, pilhas, filas e repositórios, sem perder informações sobre os tipos.
Neste artigo, vamos detalhar o que são Generics em PHP, para que servem, como usá-los corretamente na prática e examinar exemplos concretos de código.
O que são Generics e para que servem?
Generics (tipos genéricos) são um mecanismo que permite parametrizar classes, interfaces, traits e funções com um tipo de dado. Em vez de escrever uma implementação separada para cada tipo (por exemplo, IntegerList, StringList, UserList), você cria uma classe universal List<T>, onde T é um placeholder (substituto) para o tipo específico que será passado no momento do uso.
Para que serve?
- Segurança de tipos: IDEs e analisadores estáticos (Psalm, PHPStan) podem verificar se apenas elementos de um mesmo tipo entram na coleção, prevenindo erros na fase de desenvolvimento.
- Redução de duplicação de código: Você não precisa escrever a mesma classe para diferentes tipos de dados.
- Melhora na legibilidade: O código se torna autodocumentado — fica imediatamente visível quais tipos de dados são esperados.
- Aumento de desempenho: Embora o PHP em si não use Generics no nível da máquina virtual (como Java ou C#), os analisadores estáticos ajudam a encontrar erros potenciais antes da execução do código.
Como usar Generics em PHP? Sintaxe e exemplos
A partir do PHP 8.0, foi introduzido suporte para Generics no nível de anotações (docblock) e parcialmente no nível de código (para variadic arguments). O suporte completo no nível de sintaxe é esperado em versões futuras, mas já é possível usar Generics de forma eficaz com PHPDoc e analisadores estáticos.
1. Sintaxe básica com PHPDoc
A maneira mais comum é usar as anotações @template e @param/@return com a indicação do tipo genérico.
<?php
/** * @template T */class Collection { /** @var array<T> */ private array $items;
/** * @param T $item */ public function add($item): void { $this->items[] = $item; }
/** * @return T|null */ public function get(int $index) { return $this->items[$index] ?? null; }}
// Exemplo de uso$intCollection = new Collection();$intCollection->add(42);// $intCollection->add('string'); // Erro do analisador estático
$userCollection = new Collection();$userCollection->add(new User());// $userCollection->add(123); // Erro?>Neste exemplo, @template T declara o tipo genérico. @param T $item e @return T|null informam ao analisador que o tipo dos elementos deve ser consistente.
2. Restrições de tipo (bounded generics)
Você pode restringir quais tipos podem ser usados como parâmetro. Isso é feito com @extends ou @implements.
<?php
/** * @template T of \\Stringable */class StringableCollection { /** @var array<T> */ private array $items;
/** * @param T $item */ public function add($item): void { $this->it