A quoi servent les traits en PHP
#1 Qu'est-ce qu'un trait ?
Un trait fait partie d'un mécanisme qui sert à incorporer du code dans une classe. Ci-dessous, je mets du code dans le trait BarTrait
et ensuite, je l'incorpore dans la classe Foo
.
Nommez votre trait en utilisant le suffixe Trait si vous voulez respecter les PSRs.
Dit comme ça, ce mécanisme fait penser à un require. On va voir que c'est plus élaboré que ça, car il y a un mécanisme d'héritage. De plus, il est possible d'incorporer plusieurs traits, ce qui fait que l'héritage devient multiple.
Ci-dessous, pour l'explication, je laisse tomber les PSRs. :
- Je déclare un
trait A
qui contient une méthodefa
. - Je déclare un
trait B
qui contient une méthodefb
. - Je déclare un
trait C
qui incorpore letrait A
et letrait B
. - Je déclare une classe
Foo
qui incorpore letrait C
(* pas A et B). - J'instancie
Foo
et je vois que j'ai accès àfa
etfb
qui "viennent" dutrait A
et dutrait B
. Donc il y a bien un mécanisme d'héritage et de plus, il est multiple.
La classe Foo
hérite du trait C
qui lui-même hérite des traits A
et B
.
Pour autant, les traits ne sont pas des classes, car ils ne peuvent pas être instanciés et donc ne construisent aucun objet.
#2 Que peut-on mettre dans un trait ?
Dans un trait, vous pouvez mettre des propriétés, des propriétés statiques, des constantes (* depuis PHP 8.2), des méthodes, des méthodes statiques. Il y a d'autres possibilités que je n'étudierai pas ici et que vous pouvez trouver dans la doc.
Ci-dessous, il faut voir une chose. J'utilise $this
dans le trait A
alors que $this
pointe sur une instance et que le trait ne peut pas être instancié 🙃. Alors, il faut bien comprendre que ce $this
pointera sur une instance de la classe qui utilisera le trait.
#3 Gestion des collisions avec instead et as
Etant donnée que l'on a une possibilité d'héritage multiple, hé bien, on a des risques de collisions. On va voir comment gérer ces collisions.
Ci-dessous, j'ai une méthode foo()
qui est présente à la fois dans le trait A
et le trait B
. La classe Bar
utilise ces deux traits et se retrouve avec deux méthodes foo()
.
Ce qu'il faut faire pour gérer la collision c'est :
- D'abord choisir quelle est la méthode
foo()
qui va garder son nom. Ci-dessous, j'écrisA::foo insteadof B;
(* A au lieu de B). Je choisis de garder lefoo()
dutrait A
. - Ensuite, j'écris
B::foo as qux;
. Ca veut dire que pour avoir lefoo()
dutrait B
, il faut utiliserqux()
.
#4 Règles de priorité sur collision
Il y a d'autres situations qui peuvent se produire et qui ne seront pas gérables avec insteadof et as
. Dans ce cas, ce sont des règles de priorité qui vont s'appliquer.
Ci-dessous, la classe Fille
reçoit en héritage la méthode foo()
de la classe Mere
. Elle reçoit également la même méthode foo()
depuis le trait A
qu'elle utilise.
PHP va choisir la méthode venant du trait.
Ci-dessous, j'ai une classe Fille
qui reçoit une méthode foo()
de la classe Mere
et d'un trait A
. Ici, la classe Fille
surcharge la méthode foo()
. Dans ce cas, PHP va choisir le foo()
de la classe Fille
.
Ci-dessous, la classe Bar
possède la méthode foo()
et reçoit de trait A
une autre méthode foo()
. PHP va choisir celui de la classe Bar
.