Introduction : Pourquoi les développeurs PHP rêvent de génériques
\n\nSi vous avez déjà écrit en Java, C# ou TypeScript, puis êtes revenu à PHP, vous avez probablement ressenti une légère déception. Ces langages disposent d'un système élégant de types génériques (Generics), qui permet de créer des structures de données flexibles tout en étant sûres du point de vue des types. En PHP, en revanche, régnait longtemps une "anarchie typée" : les tableaux pouvaient contenir n'importe quoi, et l'IDE ne mettait en évidence que les erreurs les plus évidentes.
\n\nMais les temps changent. Depuis PHP 7.0 et surtout à l'ère de PHP 8.0/8.1/8.2, la communauté a trouvé un moyen d'utiliser les Generics en PHP. Oui, au niveau de la syntaxe du langage, ils n'existent toujours pas (et ne seront probablement pas introduits dans les années à venir), mais cela ne signifie pas que vous ne pouvez pas profiter dès aujourd'hui de tous les avantages de la programmation générique.
\n\nDans cet article, nous verrons comment fonctionne le typage en PHP avec les Generics, quels outils existent pour cela (PHPStan, Psalm), et comment écrire un code à la fois flexible et sûr. Attachez vos ceintures, ça va chauffer !
\n\nQue sont les Generics et pourquoi en a-t-on besoin en PHP ?
\n\nLes Generics (types génériques) sont un mécanisme qui permet de paramétrer des classes, des interfaces et des fonctions avec des types. Au lieu d'écrire une implémentation séparée pour chaque type de données, vous en créez une universelle, et vous spécifiez le type concret lors de l'utilisation.
\n\nImaginez que vous écrivez une classe pour une file d'attente (Queue). Sans génériques, vous ne pouvez y stocker qu'un seul type de données ou utiliser mixed (ce qui tue tout le typage). Avec les Generics, vous pouvez dire : "Ceci est une file d'entiers" ou "Ceci est une file d'objets User".
Le problème du typage "brut"
\n\nRegardez ce code PHP typique :
\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('ceci est une chaîne, pas un utilisateur'); // Erreur ! Mais PHP ne dit rien\n\n$user = $users->get(2); // Retourne une chaîne, l'IDE ne se doute de rien\n$user->getName(); // Erreur fatale à l'exécution\n\n\nC'est le problème classique du développeur PHP : l'absence de contrôle des types au moment de l'écriture du code. Vous ne découvrez l'erreur qu'à l'exécution, lorsque l'utilisateur a déjà cliqué sur le bouton. Les Generics résolvent ce problème à la racine.
\n\nGenerics en PHP : Comment ça fonctionne en pratique
\n\nPuisque la syntaxe native des Generics est absente en PHP, nous utilisons des annotations dans PHPDoc, qui sont traitées par des analyseurs statiques — PHPStan et Psalm. Ces outils vérifient le code par rapport aux types déclarés, émulant ainsi le fonctionnement des génériques.
\n\nSyntaxe de base : types paramétrés
\n\nSupposons que nous voulions rendre notre collection typée de manière sûre. Voici à quoi cela ressemble en utilisant les 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// Maintenant, utilisons-le :\n/** @var GenericCollection<User> $users */\n$users = new GenericCollection();\n$users->add(new User('Alice'));\n$users->add('chaîne'); // PHPStan générera une erreur : Type attendu User, reçu string\n\n$user = $users->get(0); // PHPStan sait que c'est un User\necho $user->getName(); //