public function creer(User&Sluggable;|null $entite): void — cette ligne ne compilait pas avant PHP 8.2. Elle illustre directement ce qu’apporte la nouvelle notation dite DNF, pour Disjunctive Normal Form : la possibilité de mélanger un type d’intersection et un type d’union dans la même déclaration.
Ce n’est pas un simple raffinement académique. Dans une méthode de fixture qui doit accepter soit un objet implémentant deux interfaces à la fois, soit une valeur nulle, cette syntaxe évite de sacrifier le typage strict au profit d’un mixed trop permissif.
Rappel : ce que PHP 8.2 change réellement
PHP 8.0 avait déjà apporté les types union (int|string). PHP 8.1 avait ajouté les types d’intersection (Countable&Iterator;), mais avec une restriction stricte : impossible de les combiner avec un type union dans la même signature. PHP 8.2 lève cette restriction en introduisant la forme normale disjonctive, à condition d’entourer chaque groupe d’intersection de parenthèses lorsqu’il est combiné à une union.
La règle syntaxique tient en une phrase : un type d’intersection combiné à une union doit être parenthésé, par exemple (A&B;)|null. Sans parenthèses, PHP refuse la déclaration avec une erreur de syntaxe.
Le cas concret d’une méthode de fixture
Dans une suite de tests, une méthode de fixture qui construit un objet de test reçoit parfois un paramètre optionnel représentant une entité qui doit respecter deux comportements à la fois. Voici une signature type, valide à partir de PHP 8.2 :
final class EntiteFixture
{
public function creer(
(Identifiable&Horodatable;)|null $source = null
): Entite {
if ($source === null) {
return new Entite(uniqid('fixture_', true));
}
return new Entite($source->getIdentifiant(), $source->getHorodatage());
}
}

Sans les types DNF, deux options existaient avant PHP 8.2 : soit typer le paramètre en mixed puis vérifier manuellement les deux interfaces avec instanceof au début de la méthode, soit créer une interface intermédiaire IdentifiableEtHorodatable qui n’a d’autre utilité que de satisfaire le typage. Les deux solutions ajoutent du code sans ajouter de valeur ; le type DNF exprime directement l’intention.
Où cela compte le plus : les doublures de test
Les fixtures qui acceptent des doublures de test (stubs, mocks) bénéficient particulièrement de cette notation. Un mock créé avec l’API de doublures de PHPUnit implémente souvent plusieurs interfaces à la fois pour satisfaire un contrat de méthode strict. Une méthode de fixture qui déclare (Countable&Traversable;)|Collection $donnees documente exactement ce qu’elle attend, sans passer par une vérification au runtime.
Cela réduit aussi la surface des tests à écrire pour la fixture elle-même : plus besoin de tester le comportement en cas de mauvais type passé, puisque PHP rejette l’appel avant même d’entrer dans la méthode, avec un TypeError explicite.
Limites à connaître
- Le type
standaloneseul (une intersection sans union, ou une union sans intersection) ne nécessite pas de parenthèses : elles ne sont obligatoires que lorsque les deux formes se combinent. - Les types DNF ne s’appliquent aujourd’hui qu’aux paramètres et aux types de retour de méthode, pas aux propriétés typées ; une extension à ces dernières est évoquée dans les discussions autour d’une prochaine version de PHP, sans certitude sur la date.
- Un IDE ou un analyseur statique ancien peut ne pas reconnaître la syntaxe si sa version est antérieure au support de PHP 8.2 ; il convient de vérifier la version de son outil d’analyse statique avant de l’adopter sur un projet entier.
Ce que cela ne remplace pas
Les types union simples introduits en PHP 8.0 restent parfaitement adaptés à la majorité des cas : un paramètre qui accepte un int ou une string n’a besoin d’aucune intersection. Le type DNF n’a d’intérêt que lorsque le paramètre doit satisfaire simultanément plusieurs interfaces, ce qui reste un cas plus rare dans une suite de tests classique que dans du code de domaine complexe.
En résumé
Les types disjonctifs de PHP 8.2 permettent de typer précisément une méthode de fixture qui accepte soit une entité respectant plusieurs interfaces à la fois, soit une valeur nulle, sans recourir à une interface artificielle ni à des vérifications manuelles. La règle à retenir est simple : parenthéser chaque groupe d’intersection dès qu’il se combine à une union, et réserver cette notation aux cas où deux contrats doivent réellement être satisfaits ensemble.