W3docs

trait

Découvrez le mot-clé trait en PHP : définir des méthodes réutilisables, combiner plusieurs traits, résoudre les conflits avec insteadof et as.

Le mot-clé PHP "trait" : Guide complet

Un trait est un mécanisme permettant de réutiliser des méthodes dans des classes indépendantes. PHP utilise l'héritage simple — une classe ne peut étendre qu'un seul parent — c'est pourquoi les traits existent pour résoudre le problème que l'héritage ne peut pas résoudre : partager le même comportement entre des classes qui n'ont pas, et ne devraient pas avoir, d'ancêtre commun. C'est ce qu'on appelle la réutilisation horizontale de code.

Un trait ressemble à une classe, mais vous ne pouvez pas l'instancier directement. À la place, vous l'utilisez avec use à l'intérieur d'une classe, et ses méthodes et propriétés sont copiées au moment de la compilation comme si vous les aviez écrites directement dans cette classe.

Cette page explique comment définir et utiliser des traits, comment en combiner plusieurs, comment résoudre les conflits de noms de méthodes, et comment les traits diffèrent de l'héritage et des interfaces.

Les traits sont une fonctionnalité POO au niveau de la classe. Si vous débutez avec les objets PHP, commencez par Classes et Objets.

Syntaxe

La syntaxe de base pour définir un trait en PHP est la suivante :

La syntaxe PHP du mot-clé trait

trait MyTrait {
  // Trait code here
}

Dans cet exemple, nous définissons un trait nommé "MyTrait" et incluons le code du trait entre les accolades.

Utilisation

Les traits peuvent être intégrés dans des classes à l'aide du mot-clé "use". Voici un exemple :

Exemple du mot-clé PHP trait

<?php

trait MyTrait
{
  public function sayHello()
  {
    echo "Hello from MyTrait!";
  }
}

class MyClass
{
  use MyTrait; // Incorporates trait methods into the class
}

$obj = new MyClass();
$obj->sayHello(); // Outputs: Hello from MyTrait!

Cet exemple définit un trait avec une méthode sayHello(). La classe MyClass intègre le trait via le mot-clé use. L'instanciation de la classe et l'appel de la méthode affichent le message du trait.

Traits multiples

Il est également possible d'intégrer plusieurs traits dans une seule classe. Voici un exemple :

Comment utiliser le mot-clé trait en PHP ?

<?php

trait TraitA
{
  public function methodA()
  {
    echo "Method A";
  }
}

trait TraitB
{
  public function methodB()
  {
    echo "Method B";
  }
}

class MyClass
{
  use TraitA, TraitB; // Incorporates both traits
}

$obj = new MyClass();
$obj->methodA(); // Outputs: Method A
$obj->methodB(); // Outputs: Method B

Ici, deux traits sont définis. MyClass intègre les deux à l'aide d'une liste séparée par des virgules dans l'instruction use. L'appel des méthodes sur une instance affiche leurs messages respectifs.

Résolution des conflits

Lorsque plusieurs traits définissent des méthodes portant le même nom, PHP génère une erreur fatale. Vous devez résoudre les conflits à l'aide des opérateurs insteadof et as.

Résolution des conflits de traits

<?php

trait TraitA {
  public function hello() {
    echo "Hello from TraitA";
  }
}

trait TraitB {
  public function hello() {
    echo "Hello from TraitB";
  }
}

class MyClass {
  use TraitA, TraitB {
    TraitA::hello insteadof TraitB; // Resolves method name collision
    TraitB::hello as helloFromB;    // Creates an alias for the overridden method
  }
}

$obj = new MyClass();
$obj->hello();        // Outputs: Hello from TraitA
$obj->helloFromB();   // Outputs: Hello from TraitB

Dans cet exemple, TraitA::hello remplace TraitB::hello pour la classe. L'opérateur as crée un alias (helloFromB) pour que la méthode originale de TraitB reste accessible. Cela évite les erreurs fatales et vous donne un contrôle total sur la priorité des méthodes.

L'opérateur as peut également modifier la visibilité d'une méthode, par exemple TraitB::hello as protected; rend la méthode importée protected à l'intérieur de la classe.

Méthodes abstraites dans les traits

Un trait peut déclarer une méthode abstraite pour exiger que toute classe l'utilisant fournisse une implémentation spécifique. Cela permet au trait d'appeler des méthodes qu'il ne définit pas lui-même — une façon légère d'imposer un contrat à la classe hôte.

Exiger une méthode de la classe hôte

<?php

trait Greetable
{
  abstract public function getName(): string;

  public function greet(): string
  {
    return "Hi, I'm " . $this->getName();
  }
}

class User
{
  use Greetable;

  public function __construct(private string $name) {}

  public function getName(): string
  {
    return $this->name;
  }
}

$user = new User("Ada");
echo $user->greet(); // Outputs: Hi, I'm Ada

Ici, greet() utilise $this->getName(), mais le trait laisse getName() abstraite. La classe User doit l'implémenter, sinon PHP génère une erreur fatale à la compilation. À l'intérieur des méthodes du trait, $this fait toujours référence à l'objet de la classe qui utilise le trait — les traits ont un accès complet aux propriétés et méthodes de cet objet.

Membres statiques dans les traits

Les traits peuvent également contenir des propriétés et méthodes statiques. Un détail important : chaque classe qui utilise le trait obtient sa propre copie indépendante de toute propriété statique — la valeur n'est pas partagée entre les différentes classes.

Un compteur statique partagé par toutes les instances d'une classe

<?php

trait Counter
{
  public static int $count = 0;

  public function increment(): void
  {
    self::$count++;
  }
}

class Widget
{
  use Counter;
}

$a = new Widget();
$b = new Widget();
$a->increment();
$b->increment();

echo Widget::$count; // Outputs: 2

Les deux instances de Widget partagent le même Widget::$count, donc le total est 2. Si une autre classe utilisait également Counter, elle suivrait son propre compteur séparé. Pour une couverture dédiée, voir Propriétés statiques.

Traits vs. Interfaces vs. Héritage

Ces trois outils POO sont faciles à confondre. Utilisez cette comparaison pour choisir le bon :

FonctionnalitéFournit une implémentation ?Combien par classe ?Idéal pour
TraitOui (méthodes concrètes + propriétés)PlusieursPartager le même code entre des classes sans lien
InterfaceNon (signatures de méthodes uniquement)PlusieursDéclarer un contrat qu'une classe doit respecter
HéritageOuiUn parentUne relation "est-un" dans une hiérarchie de classes

Un pattern courant et robuste consiste à les combiner : déclarez une interface pour le contrat public, puis fournissez un trait avec l'implémentation par défaut. Les classes implémentent l'interface et utilisent le trait avec use pour éviter de répéter le code répétitif. Les traits n'apparaissent pas dans les vérifications instanceof, c'est précisément pourquoi vous ajoutez une interface lorsque vous avez besoin de vérifications basées sur le type. Si vous avez besoin de classes de base abstraites, utilisez l'héritage.

Avantages

L'utilisation des traits en PHP présente plusieurs avantages, notamment :

  • Réutilisation du code : Les traits offrent un moyen de partager du code entre des classes sans avoir besoin de créer une nouvelle hiérarchie de classes.
  • Organisation améliorée : Les traits permettent aux développeurs d'organiser leur code de manière plus modulaire, ce qui facilite la maintenance et les mises à jour.
  • Flexibilité accrue : Les traits peuvent être intégrés dans plusieurs classes, offrant un moyen de réutiliser du code dans plusieurs projets.

Quand utiliser les traits (et quand ne pas les utiliser)

Utilisez un trait lorsque plusieurs classes ont besoin du même comportement concret mais n'appartiennent pas à une même chaîne d'héritage — par exemple un trait Loggable fournissant une méthode log() à un Controller, un PaymentGateway et un Job. Gardez les traits focalisés sur une seule responsabilité.

Soyez prudent dans quelques cas :

  • État mutable partagé. Une propriété statique public dans un trait devient effectivement globale par classe ; préférez les propriétés d'instance, sauf si vous souhaitez réellement un état partagé.
  • Couplage caché. Comme les traits sont copiés à la compilation, une méthode définie directement dans la classe prend toujours la priorité sur la version du trait, ce qui peut écraser silencieusement le comportement du trait. L'ordre de résolution des méthodes est : classe courante → trait → classe parente.
  • Suruse. Si de nombreux traits commencent à dépendre les uns des autres, c'est le signe que la logique nécessite sa propre classe (composition) à la place.

Conclusion

Le mot-clé trait vous permet de partager des méthodes concrètes et des propriétés entre des classes sans lien, contournant ainsi la limite d'héritage simple de PHP. Combinez plusieurs traits avec un use séparé par des virgules, résolvez les conflits de noms avec insteadof et as, déclarez des méthodes abstraites pour imposer un contrat, et associez les traits aux interfaces lorsque vous avez également besoin de vérifications de type. Utilisés judicieusement, les traits maintiennent votre code DRY et bien organisé.

Pratique

Pratique
Quel est le rôle des 'traits' en PHP ?
Quel est le rôle des 'traits' en PHP ?
Was this page helpful?