Comment exiger, dans la signature d’une fonction, qu’un objet remplisse deux contrats à la fois — être à la fois Countable et ArrayAccess, par exemple — sans se contenter d’un type générique object qui perdrait toute garantie ? Avant PHP 8.1, la réponse habituelle consistait à créer une interface intermédiaire qui hérite des deux, uniquement pour ce besoin de typage. PHP 8.1 introduit une solution plus directe : les types d’intersection.
Cette fonctionnalité répond à un besoin différent des types union apparus avec PHP 8.0 : là où un type union autorise l’une ou l’autre des interfaces indiquées, un type d’intersection exige la conformité aux deux simultanément.
La syntaxe et sa signification
Un type d’intersection s’écrit avec le symbole & entre deux noms de types, par exemple Countable&ArrayAccess. PHP vérifiera, à l’exécution, que l’objet passé implémente bien les deux interfaces mentionnées — pas une combinaison de classes concrètes, cette syntaxe étant réservée aux interfaces et aux classes abstraites, jamais aux types scalaires comme int ou string.
Cette fonctionnalité s’avère particulièrement utile dans la conception d’une API interne d’extension, quand un composant doit accepter un objet qui se comporte à la fois comme une collection dénombrable et comme un tableau accessible par clé, sans imposer une classe concrète précise à l’appelant.
Un exemple : une API interne d’extension

Imaginons une extension de gestion de panier qui définit deux interfaces distinctes, l’une pour le comptage, l’autre pour l’accès par clé, et une méthode qui exige les deux à la fois :
interface PanierDenombrable {
public function compterArticles(): int;
}
interface PanierAccessibleParCle {
public function obtenirArticle( string $cle );
}
class GestionnairePanier {
public function afficherResume( PanierDenombrable&PanierAccessibleParCle $panier ): string {
$nombre = $panier->compterArticles();
return sprintf(
/* translators: %d : nombre d'articles dans le panier */
__( '%d article(s) dans le panier', 'boutique-interne' ),
$nombre
);
}
}
N’importe quelle classe implémentant à la fois PanierDenombrable et PanierAccessibleParCle peut être passée à afficherResume(), sans qu’il soit nécessaire de créer une troisième interface, PanierComplet, qui hériterait des deux uniquement pour satisfaire la signature de cette méthode.
Ce que les types d’intersection ne remplacent pas
- Ils ne remplacent pas les types union, introduits par PHP 8.0, qui répondent à un besoin inverse : accepter l’un ou l’autre type, pas les deux ensemble.
- Ils ne peuvent pas mélanger un type d’intersection et un type union dans une même déclaration en PHP 8.1 : cette combinaison, appelée type disjonctif normal, n’arrivera qu’avec PHP 8.2.
- Ils ne s’appliquent qu’aux interfaces et classes, jamais aux types scalaires ni au type
null, qui ne peut pas être combiné dans une intersection.
Quand préférer une interface unique classique
Les types d’intersection ne doivent pas devenir un réflexe systématique. Si deux interfaces sont presque toujours utilisées ensemble dans l’ensemble du projet, il reste souvent plus lisible de définir une véritable interface combinée, avec un nom métier clair, plutôt que de répéter InterfaceA&InterfaceB dans chaque signature de méthode concernée. Le type d’intersection convient mieux à un besoin ponctuel, localisé à une ou deux méthodes.
Réserver les types d’intersection aux combinaisons ponctuelles : si la même paire d’interfaces revient dans toute l’API d’une extension, une interface combinée nommée reste plus lisible qu’une répétition du type d’intersection partout.
Compatibilité à vérifier
Comme pour toute syntaxe apparue avec PHP 8.1, cette fonctionnalité impose un minimum de version PHP 8.1 sur l’hébergement ciblé. Une extension qui viserait encore une compatibilité avec PHP 8.0 ou une version antérieure ne peut pas utiliser cette syntaxe, et devra se contenter d’une interface intermédiaire classique ou d’une vérification via instanceof dans le corps de la méthode.
En résumé
Les types d’intersection de PHP 8.1 répondent à un besoin de conception précis : exiger qu’un paramètre remplisse plusieurs contrats à la fois, sans passer par une interface intermédiaire créée uniquement pour cet usage. Ils complètent, sans les remplacer, les types union apparus l’année précédente, et trouvent leur place dans la conception d’API internes d’extension où deux comportements doivent cohabiter sur un même objet.