Generics en PHP: guía completa de tipificación con ejemplos

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

¿Por qué los Generics no son solo una moda, sino una necesidad?

\n\n

Si alguna vez ha escrito código PHP que trabajaba con colecciones, repositorios o la capa de servicios, seguramente se ha encontrado con esta situación: una función acepta array, pero ¿qué hay dentro? Es un misterio. Puede que haya usuarios, o puede que haya gatos. O, peor aún, ambos. Aquí es donde los Generics (genéricos) vienen al rescate: una poderosa herramienta de tipificación que hace que su código sea predecible, seguro y autodocumentado.

\n\n

En este artículo, analizaremos cómo funcionan los generics en PHP, para qué sirven y cómo usarlos en proyectos reales. Aprenderá a evitar errores típicos y a hacer que su código sea profesional. ¡Allá vamos!

\n\n

¿Qué son los Generics en palabras sencillas?

\n\n

Imagínese que es un desarrollador de una tienda en línea. Tiene una clase Cart que almacena productos. Sin generics, escribe:

\n\n
class Cart {\n    private array $items;\n    \n    public function addItem($item): void {\n        $this->items[] = $item;\n    }\n    \n    public function getItems(): array {\n        return $this->items;\n    }\n}
\n\n

¿El problema? $item puede ser cualquier cosa: un número, una cadena, un objeto Product o incluso null. El IDE no le dirá qué tipo devuelve getItems(). Tendrá que revisar la documentación o depurar.

\n\n

Los Generics resuelven este problema. Permiten especificar: "¡Oye, esta clase solo funciona con productos!". Se ve así:

\n\n
class Cart {\n    /** @param Product[] $items */\n    private array $items;\n    \n    public function addItem(Product $item): void {\n        $this->items[] = $item;\n    }\n    \n    /** @return Product[] */\n    public function getItems(): array {\n        return $this->items;\n    }\n}
\n\n

Pero esto es solo una simulación. Los verdaderos generics en PHP aparecieron con PHP 8.0, y el soporte completo llegó en PHP 8.1+ mediante atributos y análisis estático (por ejemplo, PHPStan o Psalm).

\n\n

¿Cómo funcionan realmente los Generics en PHP?

\n\n

A diferencia de Java o C#, PHP no tiene soporte nativo de generics a nivel de sintaxis (todavía). Sin embargo, gracias a las anotaciones PHPDoc y las herramientas de análisis estático, podemos obtener todas las ventajas de la tipificación.

\n\n

Tipos de plantilla (Template Types)

\n\n

El enfoque más popular es usar @template en PHPDoc. Veamos un ejemplo con una colección:

\n\n
/**\n * @template T\n */\nclass Collection {\n    /** @var array<int, T> */\n    private array $items = [];\n    \n    /**\n     * @param T $item\n     */\n    public function add(mixed $item): void {\n        $this->items[] = $item;\n    }\n    \n    /**\n     * @return T\n     */\n    public function get(int $index): mixed {\n        return $this->items[$index] ?? throw new \\OutOfBoundsException();\n    }\n}
\n\n

Ahora, al usar esta clase, especifica el tipo concreto:

\n\n
/** @var Collection<User> $userCollection */\n$userCollection = new Collection();\n$userCollection->add(new User('Alice'));\n$user = $userCollection->get(0); // PHPStan sabe: $user es User\necho $user->name; // ¡Seguro!
\n\n

Restricciones (Constraints) para Generics

\n\n

A veces es necesario que el tipo T no sea cualquiera, sino que implemente una interfaz específica. Esto se llama bound (restricción):

\n\n
/**\n * @template T of \\JsonSerializable\n */\nclass JsonCollection {\n    /** @var array<int, T> */\n    private array $items = [];\n    \n    public function toJson(): string {\n        return json_encode($this->items);\n    }\n}
\n\n

Ahora no podrá agregar a la colección un objeto que no implemente JsonSerializable. El IDE y el analizador estático mostrarán un error.

\n\n

Ejemplos prácticos: de lo simple a lo complejo

Blogs

Book Recommendations