Les Traits en PHP, un outil puissant trop peu utilisé ?


PHP ne permet pas l’héritage multiple. Pour contourner cette limite, les Traits ont été introduits à partir de PHP 5.4. Ils permettent d’injecter des méthodes dans plusieurs classes sans les faire hériter d’une même classe parente.

Mal utilisés, ils peuvent nuire à la lisibilité et à la maintenabilité. Bien maîtrisés, ils sont un excellent outil pour structurer du code partagé. Voici un tour d’horizon des usages, bonnes pratiques et pièges à éviter.


1. Pourquoi les Traits ?

En PHP, une classe ne peut hériter que d’une seule autre classe. Les Traits permettent de factoriser du comportement réutilisable dans des classes différentes.

trait LoggerTrait {
    public function log(string $message): void {
        echo "[LOG] $message\n";
    }
}

class ServiceA {
    use LoggerTrait;
}

class ServiceB {
    use LoggerTrait;
}

2. Cas d’usage concrets

Les Traits sont utiles pour encapsuler des comportements transverses.

Logger partagé

trait LoggerTrait {
    public function log(string $message): void {
        echo date('Y-m-d H:i:s') . " - $message\n";
    }
}

Suppression logique (soft delete)

trait SoftDeleteTrait {
    private ?\DateTime $deletedAt = null;

    public function delete(): void {
        $this->deletedAt = new \DateTime();
    }

    public function isDeleted(): bool {
        return $this->deletedAt !== null;
    }
}

3. Bonnes pratiques

Un Trait = une responsabilité
Éviter les traits fourre-tout. Chaque Trait doit avoir un rôle précis.

Méthodes explicites
Des noms clairs évitent les collisions. Préférer handleForm() à handle().

Pas de dépendances implicites
Un Trait ne doit pas supposer l’existence d’une propriété ou méthode sans la définir ou l’injecter clairement.

// Mauvais
trait BrokenTrait {
    public function doSomething(): void {
        $this->repository->save(); // dépendance cachée
    }
}
// Correct
trait WellDefinedTrait {
    protected RepositoryInterface $repository;

    public function setRepository(RepositoryInterface $repo): void {
        $this->repository = $repo;
    }

    public function doSomething(): void {
        $this->repository->save();
    }
}

4. Pièges à éviter

Collisions de méthodes
Deux traits avec une méthode du même nom nécessitent une résolution explicite.

class C {
    use A, B {
        B::sayHello insteadof A;
    }
}

Utilisation excessive
Accumuler trop de Traits dans une même classe nuit à la clarté.

Fausse abstraction
Les Traits ne remplacent pas un vrai design orienté objet. Si vous avez besoin de polymorphisme ou de hiérarchie, préférez les interfaces ou classes abstraites.


5. Alternatives

  • Interface + classe abstraite : plus contraignant, mais plus clair.
  • Composition : déléguer les responsabilités à d’autres objets.
  • Services dédiés : dans Symfony, injecter un service est souvent plus propre.

Conclusion

Les Traits sont un outil utile pour structurer du code partagé, à condition de les utiliser avec modération et rigueur.

En résumé :

  • Limitez leur responsabilité.
  • Évitez les dépendances implicites.
  • Préférez la composition dès que les Traits deviennent lourds.

Bien utilisés, les Traits simplifient. Mal utilisés, ils complexifient. À vous de trancher.

Et vous ?

Connaissiez-vous les traits en PHP ? Partagez vos retours ou astuces en me rejoignant sur LinkedIn — je serai ravi d’en discuter !