Generics en PHP: para qué sirven y cómo usarlos
Los tipos genéricos (Generics) son una de las características más esperadas y discutidas en el mundo de PHP. Aunque oficialmente aún no están completamente implementados en el lenguaje a partir de PHP 8.x, la comunidad utiliza activamente enfoques alternativos y espera un soporte completo. En este artículo analizaremos para qué sirven los Generics en PHP, cómo se pueden emular y qué ventajas aportan al desarrollador.
¿Qué son los Generics y para qué sirven?
Los Generics (generalizaciones) permiten crear clases, interfaces y funciones que trabajan con diferentes tipos de datos, manteniendo al mismo tiempo una tipificación estricta. En lenguajes con tipificación estática (Java, C#, TypeScript) ya son un estándar desde hace tiempo. Para PHP, que originalmente era de tipificación dinámica, los Generics representan un paso hacia un código más fiable y legible.
Principales problemas que resuelven los Generics:
- Seguridad de tipos — eliminan errores relacionados con el envío de datos de tipo incorrecto.
- Reducción de duplicación de código — no es necesario escribir clases separadas para cada tipo.
- Mejora de la legibilidad — se entiende inmediatamente con qué tipos trabaja una función o clase.
- Soporte de IDE — el autocompletado y el análisis de código son más precisos.
El problema sin Generics: ejemplo en PHP
Imagina que necesitas crear una colección que almacene solo números enteros. Sin Generics puedes escribir:
class IntegerCollection { private array $items = [];
public function add(int $item): void { $this->items[] = $item; }
public function get(int $index): int { return $this->items[$index]; }}Funciona, pero si necesitas una colección para cadenas, tendrás que copiar el código y cambiar los tipos. Y si necesitas una colección para objetos, ocurre lo mismo. Esto provoca duplicación y aumento del volumen de código.
Emulación de Generics en PHP con anotaciones y PHPDoc
Actualmente PHP no soporta Generics completos a nivel de sintaxis, pero se pueden emular mediante anotaciones PHPDoc y bibliotecas especiales (por ejemplo, phpstan/phpstan o vimeo/psalm).
Ejemplo de una clase genérica con anotaciones:
/** * @template T */class Collection { private array $items = [];
/** * @param T $item */ public function add($item): void { $this->items[] = $item; }
/** * @return T */ public function get(int $index) { return $this->items[$index]; }}Ahora, al usar la clase, puedes especificar el tipo concreto:
/** @var Collection<int> $intCollection */$intCollection = new Collection();$intCollection->add(42);$value = $intCollection->get(0); // IDE sabe que es intLos analizadores estáticos (PHPStan, Psalm) verificarán que no estás pasando una cadena a una colección de números enteros. Esto proporciona casi la misma seguridad que los Generics completos.
Ejemplos reales de uso de Generics en PHP
Consideremos varios escenarios prácticos donde los Generics son especialmente útiles.
1. Repositorios en Doctrine
En Doctrine ORM a menudo trabajas con repositorios. Los Generics ayudan a indicar explícitamente con qué entidad trabaja el repositorio:
/** * @template T of object */interface RepositoryInterface { /** * @param int $id * @return T|null */ public function find(int $id): ?object;}
/** * @implements RepositoryInterface<User> */class UserRepository implements RepositoryInterface { public function find(int $id): ?User { // lógica de búsqueda }}2. Patrón "Estrategia" con generalizaciones
Los Generics permiten crear