Menu

PHP Traits: use, mehrere Traits und insteadof

Ein PHP-Trait ist ein wiederverwendbarer Block aus Methoden und Eigenschaften, den du mit use TraitName; in eine Klasse kopierst. Hier: mehrere Traits verwenden, Namenskonflikte mit insteadof und as lösen, abstrakte und statische Trait-Member, welche Methode gewinnt und Trait vs Interface vs abstrakte Klasse.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Ein PHP-Trait ist ein wiederverwendbarer Block aus Methoden und Eigenschaften. Du deklarierst ihn mit trait Name { ... } und bindest ihn mit use Name; im Klassenrumpf in jede Klasse ein. PHP kopiert die Member des Traits in die Klasse, sodass sie genau so funktionieren, als hättest du sie dort geschrieben.

User und Robot haben keine gemeinsame Elternklasse, und trotzdem haben jetzt beide hello(). Genau für diesen Fall gibt es Traits: PHP-Klassen können nur eine Klasse erweitern, und ein Trait teilt Code seitwärts, zwischen Klassen, die nicht verwandt sind.

Traits mit Eigenschaften

Ein Trait kann neben seinen Methoden Eigenschaften mitbringen. Die Methoden verwenden $this wie jede Methode der Klasse, denn sobald der Trait hineinkopiert ist, sind sie Methoden der Klasse:

Eine Klasse darf keine Eigenschaft mit demselben Namen wie eine Trait-Eigenschaft deklarieren, außer beide Deklarationen sind identisch (gleiche Sichtbarkeit, gleicher Typ, gleicher Standardwert); sonst ist es ein fataler Fehler.

Mehrere Traits verwenden

Führe mehrere Traits in einer use-Anweisung auf, durch Kommas getrennt, oder schreib mehrere use-Zeilen. Die Klasse bekommt die Member aller:

Konflikte mit insteadof und as lösen

Wenn zwei Traits eine Methode mit demselben Namen definieren, weigert sich PHP zu raten und stoppt mit einem fatalen Fehler. In einem use-Block wählt insteadof den Gewinner, und as behält die unterlegene Methode unter einem anderen Namen:

Mit einem einfachen use EnglishGreeting, FrenchGreeting; ohne Block stoppt das Skript, bevor irgendetwas ausgegeben wird:

PHP Fatal error:  Trait method FrenchGreeting::greet has not been applied as Host::greet, because of collision with EnglishGreeting::greet in /home/index.php on line 12

as kann auch die Sichtbarkeit ändern. use Loggable { log as protected; } macht das log() des Traits nur in dieser Klasse protected, und use Loggable { log as private writeLog; } fügt einen privaten Alias hinzu.

Abstrakte und statische Member in einem Trait

Ein Trait kann von der verwendenden Klasse etwas verlangen, indem er eine abstrakte Methode deklariert. Er kann auch statische Methoden und statische Eigenschaften haben. Jede Klasse, die den Trait verwendet, bekommt ihre eigene Kopie einer statischen Eigenschaft:

Car und Bike zählen getrennt, weil die statische Eigenschaft in jede Klasse kopiert wird. Das ist anders als bei einer statischen Eigenschaft in einer gemeinsamen Elternklasse, die sich alle Kinder teilen; die Seite zu static zeigt diesen Fall.

Welche Methode gewinnt: Klasse, Trait oder Elternklasse

Existiert derselbe Methodenname in der Klasse selbst, in einem verwendeten Trait und in ihrer Elternklasse, wählt PHP in dieser Reihenfolge: die eigene Methode der Klasse, dann die des Traits, dann die der Elternklasse. Im Trait erreicht parent:: weiterhin die Version der Elternklasse:

Ein Trait ist kein Typ

instanceof passt nie auf einen Trait, und ein Parameter mit einem Trait-Namen als Typ lehnt jedes Objekt mit einem TypeError ab. Um zu fragen, „ob diese Klasse das Verhalten hat“, kombinierst du den Trait mit einem Interface oder untersuchst die Klasse mit class_uses():

Das Muster oben ist das übliche: Das Interface Exportable ist das, was Funktionen als Typangabe verwenden und instanceof prüft, und der Trait ExportsProperties ist eine fertige Implementierung, die Klassen verwenden oder ersetzen können. Ein Trait allein ist geteilter Code ohne Vertrag; wenn du merkst, dass du class_uses() in der Geschäftslogik prüfst, wollte der Trait ein Interface neben sich. Für Code, den eine Familie verwandter Klassen teilt, ist eine abstrakte Klasse vielleicht die einfachere Wahl.

Häufig gestellte Fragen

Was ist ein Trait in PHP?

Ein Trait ist eine benannte Gruppe von Methoden (und Eigenschaften), deklariert mit trait Name { }, die eine Klasse mit use Name; einbindet. PHP kopiert die Member des Traits in die Klasse, sodass sie sich verhalten, als hättest du sie dort geschrieben. So teilst du Code zwischen Klassen, die keine gemeinsame Elternklasse haben.

Kann eine PHP-Klasse mehrere Traits verwenden?

Ja: use Timestamps, SoftDeletes; oder mehrere use-Zeilen. Definieren zwei Traits eine Methode mit demselben Namen, musst du mit insteadof eine wählen, zum Beispiel use A, B { A::hello insteadof B; }, und kannst die andere mit B::hello as helloB; unter neuem Namen behalten.

Was ist der Unterschied zwischen einem Trait und einem Interface in PHP?

Ein Interface ist ein Typ ohne Code: Es sagt, welche Methoden eine Klasse haben muss, und instanceof funktioniert damit. Ein Trait ist Code ohne Typ: Er fügt Methodenrümpfe in eine Klasse ein, aber $obj instanceof SomeTrait ist immer falsch. Oft kombiniert man beides: ein Interface für den Typ, ein Trait mit einer Standardimplementierung.

Kann ein Trait einen Konstruktor oder abstrakte Methoden haben?

Beides ja. Ein Trait kann abstract public function name(): string; deklarieren, um von der verwendenden Klasse diese Methode zu verlangen, und er kann __construct definieren, auch wenn ein Konstruktor in einem Trait leicht mit dem der Klasse kollidiert und selten eine gute Idee ist.

Welche Methode gewinnt, wenn eine Klasse und ein Trait dieselbe Methode definieren?

Die eigene Methode der Klasse gewinnt gegen die des Traits, und die Methode des Traits gewinnt gegen eine von der Elternklasse geerbte Methode. Die Reihenfolge ist also: Klasse, dann Trait, dann Elternklasse.

Illustration der Programmiersprachen bei Coddy

Lerne mit Coddy zu programmieren

LOS GEHT'S