Lesson 5/6 8 min

Traits

Reuse code horizontally across unrelated classes. PHP's answer to the lack of multiple inheritance, with runnable code.

FR EN

Le problème : du code commun, mais pas de parent commun

Imagine deux classes très différentes, Article et Utilisateur, qui ont toutes deux besoin d'écrire dans un journal (log). Les faire hériter d'une même classe Journalisable serait absurde (un article n'« est pas » la même famille qu'un utilisateur), et de toute façon PHP n'autorise qu'un seul parent.

La solution : un trait. C'est un bloc de méthodes réutilisable qu'on « copie » dans n'importe quelle classe avec use, sans relation de parenté.

Définir et utiliser un trait

trait Journalisable {
    public function log(string $message): void {
        echo "[" . static::class . "] $message\n";
    }
}

class Article {
    use Journalisable;              // on "branche" le trait
    public function publier(): void {
        $this->log("Article publié");
    }
}
class Utilisateur {
    use Journalisable;              // le même trait, dans une classe sans lien
    public function connecter(): void {
        $this->log("Utilisateur connecté");
    }
}

Les deux classes obtiennent la méthode log() sans rien dupliquer et sans héritage. Une classe peut utiliser plusieurs traits (use A, B;).

Trait vs héritage vs interface

  • Interface : un contrat (quelles méthodes), sans code.
  • Héritage : une relation « est un », un seul parent, qui apporte du code.
  • Trait : du code à partager horizontalement, sans relation « est un », autant qu'on veut.

À ne pas surutiliser : un trait qui touche aux propriétés de la classe crée un couplage caché. Garde-les petits et autonomes (du comportement réutilisable bien identifié, comme le log).

Exécute : un trait partagé

<?php
trait Journalisable {
    public function log(string $message): void {
        echo "[" . static::class . "] $message\n";
    }
}
class Article {
    use Journalisable;
    public function publier(): void { $this->log("Article publié"); }
}
class Utilisateur {
    use Journalisable;
    public function connecter(): void { $this->log("Utilisateur connecté"); }
}

(new Article())->publier();        // [Article] Article publié
(new Utilisateur())->connecter();  // [Utilisateur] Utilisateur connecté
?>
À quoi sert un trait en PHP ?
Combien de traits une classe peut-elle utiliser ?
Prochaine étape

Tu sais écrire des classes solides. Reste à les organiser dans un vrai projet : éviter les collisions de noms et les charger automatiquement. C'est le rôle des namespaces.

Leçon 6 : namespaces →

The problem: shared code, but no shared parent

Picture two very different classes, Article and Utilisateur, both needing to write to a log. Making them inherit from a common Journalisable class would be absurd (an article isn't the same family as a user), and PHP allows only one parent anyway.

The solution: a trait. It's a reusable block of methods that you "copy" into any class with use, with no parent relationship.

Defining and using a trait

trait Journalisable {
    public function log(string $message): void {
        echo "[" . static::class . "] $message\n";
    }
}

class Article {
    use Journalisable;              // we "plug in" the trait
    public function publier(): void {
        $this->log("Article published");
    }
}
class Utilisateur {
    use Journalisable;              // the same trait, in an unrelated class
    public function connecter(): void {
        $this->log("User logged in");
    }
}

Both classes get the log() method with no duplication and no inheritance. A class can use several traits (use A, B;).

Trait vs inheritance vs interface

  • Interface: a contract (which methods), no code.
  • Inheritance: an "is a" relationship, a single parent, bringing code.
  • Trait: code to share horizontally, no "is a" relationship, as many as you want.

Don't overuse: a trait touching the class's properties creates hidden coupling. Keep them small and self-contained (well-identified reusable behavior, like logging).

Run it: a shared trait

<?php
trait Journalisable {
    public function log(string $message): void {
        echo "[" . static::class . "] $message\n";
    }
}
class Article {
    use Journalisable;
    public function publier(): void { $this->log("Article published"); }
}
class Utilisateur {
    use Journalisable;
    public function connecter(): void { $this->log("User logged in"); }
}

(new Article())->publier();        // [Article] Article published
(new Utilisateur())->connecter();  // [Utilisateur] User logged in
?>
Next step

You can write solid classes. Now you must organize them in a real project: avoid name collisions and load them automatically. That's the role of namespaces.

Lesson 6: namespaces →
Rappel libre

Sans remonter dans la leçon : avec tes mots, qu'est-ce qu'un trait, et en quoi diffère-t-il de l'héritage ?

Un trait est un bloc de méthodes réutilisable qu'on injecte dans une classe avec use, sans relation de parenté. Différence clé avec l'héritage : pas de lien « est un » et surtout pas de limite à un seul (use A, B, C;), alors qu'une classe n'a qu'un seul parent. Le trait partage du comportement horizontalement entre des classes sans rapport (ex. log() dans Article et Utilisateur).
Accepter ou rejeter le code de l'IA

L'IA propose ce trait pour partager une connexion à la base entre tes classes. Ton rôle de relecteur : l'accepter tel quel ou le rejeter, et dire pourquoi.

trait BaseDeDonnees {
    public $pdo;
    public function connecter(): void {
        $this->pdo = new PDO("mysql:host=localhost;dbname=app", "root", "");
    }
}
Rejeter. Deux problèmes cumulés. D'abord la propriété public $pdo : n'importe quel code extérieur peut la réécrire ($obj->pdo = null;), c'est une visibilité abusive, mets-la private ou protected. Ensuite, c'est exactement le piège annoncé dans la leçon : un trait qui touche aux propriétés de la classe crée un couplage caché (toute classe qui l'utilise hérite silencieusement d'un $pdo, et les identifiants sont en dur). Un trait doit rester petit et autonome (du comportement, comme log()). Ici, une dépendance vers un service de connexion injecté serait plus saine.
Besoin d'un développeur pour votre projet ?

Réponse sous 24h · Sans engagement