W3docs

trait

PHP trait-Schlüsselwort: wiederverwendbare Methoden definieren, mehrere Traits kombinieren, Konflikte mit insteadof und as lösen.

Das PHP-Schlüsselwort „trait": Ein umfassender Leitfaden

Ein Trait ist ein Mechanismus zur Wiederverwendung von Methoden in unabhängigen Klassen. PHP verwendet Einzelvererbung — eine Klasse kann nur eine einzige Elternklasse erweitern —, weshalb Traits existieren, um das Problem zu lösen, das Vererbung nicht lösen kann: dasselbe Verhalten unter Klassen zu teilen, die keine gemeinsame Basisklasse haben und auch nicht haben sollten. Dies wird als horizontale Code-Wiederverwendung bezeichnet.

Ein Trait sieht wie eine Klasse aus, kann jedoch nicht eigenständig instanziiert werden. Stattdessen wird er mit use in eine Klasse eingebunden, und seine Methoden und Eigenschaften werden zur Kompilierzeit eingefügt, als wären sie direkt in der Klasse geschrieben worden.

Diese Seite behandelt, wie man Traits definiert und verwendet, wie man mehrere davon kombiniert, wie man Methodennamen-Konflikte auflöst und wie sich Traits von Vererbung und Interfaces unterscheiden.

Traits sind ein OOP-Feature auf Klassenebene. Wenn Sie neu in PHP-Objekten sind, beginnen Sie zunächst mit Klassen und Objekte.

Syntax

Die grundlegende Syntax zur Definition eines Traits in PHP lautet wie folgt:

Die PHP-Syntax des trait-Schlüsselworts

trait MyTrait {
  // Trait code here
}

In diesem Beispiel definieren wir einen Trait namens „MyTrait" und fügen den Trait-Code innerhalb der geschweiften Klammern ein.

Verwendung

Traits können mit dem Schlüsselwort „use" in Klassen eingebunden werden. Hier ist ein Beispiel:

Beispiel des PHP-trait-Schlüsselworts

<?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!

Dieses Beispiel definiert einen Trait mit einer sayHello()-Methode. Die Klasse MyClass bindet den Trait über das Schlüsselwort use ein. Das Instanziieren der Klasse und der Aufruf der Methode gibt die Nachricht des Traits aus.

Mehrere Traits

Es ist auch möglich, mehrere Traits in eine einzelne Klasse einzubinden. Hier ist ein Beispiel:

Wie verwendet man das trait-Schlüsselwort in 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

Hier werden zwei Traits definiert. MyClass bindet beide über eine kommagetrennte Liste in der use-Anweisung ein. Der Aufruf der Methoden auf einer Instanz gibt die jeweiligen Nachrichten aus.

Konfliktauflösung

Wenn mehrere Traits Methoden mit demselben Namen definieren, löst PHP einen fatalen Fehler aus. Konflikte müssen mit den Operatoren insteadof und as aufgelöst werden.

Auflösung von Trait-Konflikten

<?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

In diesem Beispiel ersetzt TraitA::hello die Methode TraitB::hello für die Klasse. Der Operator as erstellt einen Alias (helloFromB), sodass die ursprüngliche TraitB-Methode weiterhin zugänglich bleibt. Dies verhindert fatale Fehler und gibt Ihnen volle Kontrolle über die Methodenpriorität.

Der Operator as kann auch die Sichtbarkeit einer Methode ändern, beispielsweise macht TraitB::hello as protected; die importierte Methode innerhalb der Klasse protected.

Abstrakte Methoden in Traits

Ein Trait kann eine abstrakte Methode deklarieren, um zu verlangen, dass jede Klasse, die ihn verwendet, eine bestimmte Implementierung bereitstellt. Dadurch kann der Trait Methoden aufrufen, die er selbst nicht definiert — eine schlanke Möglichkeit, einen Vertrag für die verwendende Klasse durchzusetzen.

Eine Methode von der verwendenden Klasse verlangen

<?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

Hier verwendet greet() $this->getName(), aber der Trait lässt getName() abstrakt. Die Klasse User muss sie implementieren, andernfalls löst PHP zur Kompilierzeit einen fatalen Fehler aus. Innerhalb von Trait-Methoden bezieht sich $this immer auf das Objekt der Klasse, die den Trait verwendet — Traits haben vollen Zugriff auf die Eigenschaften und Methoden dieses Objekts.

Statische Member in Traits

Traits können auch statische Eigenschaften und Methoden enthalten. Ein wichtiges Detail: Jede Klasse, die den Trait verwendet, erhält eine eigene unabhängige Kopie jeder statischen Eigenschaft — der Wert wird nicht zwischen verschiedenen Klassen geteilt.

Ein statischer Zähler, der von allen Instanzen einer Klasse geteilt wird

<?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

Beide Instanzen von Widget teilen dasselbe Widget::$count, sodass die Gesamtsumme 2 beträgt. Wenn eine andere Klasse ebenfalls Counter verwendet, würde sie ihren eigenen separaten Zähler verwalten. Für eine dedizierte Behandlung siehe Statische Eigenschaften.

Traits vs. Interfaces vs. Vererbung

Diese drei OOP-Werkzeuge werden leicht verwechselt. Verwenden Sie diesen Vergleich, um das richtige auszuwählen:

MerkmalBietet Implementierung?Wie viele pro Klasse?Am besten geeignet für
TraitJa (konkrete Methoden + Eigenschaften)VieleDenselben Code unter nicht verwandten Klassen teilen
InterfaceNein (nur Methodensignaturen)VieleEinen Vertrag deklarieren, den eine Klasse erfüllen muss
VererbungJaEine ElternklasseEine „ist-ein"-Beziehung in einer Klassenhierarchie

Ein verbreitetes, robustes Muster ist die Kombination beider: ein Interface für den öffentlichen Vertrag deklarieren und dann einen Trait mit der Standardimplementierung bereitstellen. Klassen implementieren das Interface und verwenden den Trait mit use, sodass sie den Boilerplate-Code nicht wiederholen müssen. Traits erscheinen nicht in instanceof-Prüfungen, weshalb man ein Interface hinzufügt, wenn typbasierte Prüfungen benötigt werden. Wenn stattdessen abstrakte Basisklassen benötigt werden, greift man zur Vererbung.

Vorteile

Die Verwendung von Traits in PHP bietet mehrere Vorteile, darunter:

  • Code-Wiederverwendung: Traits bieten eine Möglichkeit, Code zwischen Klassen zu teilen, ohne eine neue Klassenhierarchie erstellen zu müssen.
  • Verbesserte Organisation: Traits ermöglichen es Entwicklern, ihren Code modularer zu organisieren, was die Pflege und Aktualisierung erleichtert.
  • Erhöhte Flexibilität: Traits können in mehrere Klassen eingebunden werden und bieten so eine Möglichkeit, Code über mehrere Projekte hinweg wiederzuverwenden.

Wann Traits verwendet werden sollten (und wann nicht)

Greifen Sie zu einem Trait, wenn mehrere Klassen genau dasselbe konkrete Verhalten benötigen, aber nicht zu einer Vererbungskette gehören — zum Beispiel ein Loggable-Trait, der einem Controller, einem PaymentGateway und einem Job eine log()-Methode gibt. Halten Sie Traits auf eine einzige Verantwortung fokussiert.

In einigen Fällen ist Vorsicht geboten:

  • Gemeinsamer veränderlicher Zustand. Eine public statische Eigenschaft in einem Trait wird effektiv global pro Klasse; bevorzugen Sie Instanzeigenschaften, es sei denn, Sie möchten tatsächlich gemeinsamen Zustand.
  • Versteckte Kopplung. Da Traits zur Kompilierzeit eingefügt werden, überschreibt eine direkt in der Klasse definierte Methode immer die Trait-Version, was das Trait-Verhalten still überschreiben kann. Die Methodenauflösungsreihenfolge lautet: aktuelle Klasse → Trait → Elternklasse.
  • Übermäßiger Einsatz. Wenn viele Traits anfangen, voneinander abhängig zu sein, ist das ein Zeichen dafür, dass die Logik eine eigene Klasse (Komposition) benötigt.

Fazit

Das Schlüsselwort trait ermöglicht es, konkrete Methoden und Eigenschaften über nicht verwandte Klassen hinweg zu teilen und umgeht dabei PHPs Einzelvererbungsbeschränkung. Kombinieren Sie mehrere Traits mit einer kommagetrennte use-Liste, lösen Sie Namenskonflikte mit insteadof und as, deklarieren Sie abstrakte Methoden zur Durchsetzung eines Vertrags und kombinieren Sie Traits mit Interfaces, wenn auch Typprüfungen benötigt werden. Gezielt eingesetzt halten Traits Ihren Code DRY und gut organisiert.

Übungen

Übung
Was ist die Aufgabe von 'Traits' in PHP?
Was ist die Aufgabe von 'Traits' in PHP?
Was this page helpful?