W3docs

PHP Objektorientierte Programmierung: Traits verstehen

PHP Traits deklarieren und verwenden, mehrere Traits kombinieren, Methodenkonflikte mit insteadof und as lösen sowie abstrakte und statische Member nutzen.

Ein Trait ist ein wiederverwendbarer Block aus Methoden (und Eigenschaften), den Sie in jede beliebige Klasse einmischen können. Traits lösen ein spezifisches Problem in PHP: Eine Klasse kann nur eine Elternklasse erweitern. Wenn zwei nicht verwandte Klassen dasselbe Verhalten teilen müssen, reicht einfache Vererbung nicht aus. Traits ermöglichen es, dieses Verhalten horizontal zu teilen — über Klassen hinweg, die keine Eltern-Kind-Beziehung haben.

Dieses Kapitel erklärt, was Traits sind, wie man sie deklariert und verwendet, wie man mehrere Traits kombiniert, wie PHP Methodennamen-Konflikte auflöst und welche Fallstricke (abstrakte Methoden, statische Member, Eigenschaften) häufig zu Problemen führen.

Was ist ein Trait?

Ein Trait sieht fast genauso aus wie eine Klasse, wird aber mit dem Schlüsselwort trait statt class deklariert. Die wesentlichen Unterschiede:

  • Ein Trait kann nicht eigenständig instanziiert werden — es gibt kein new MyTrait().
  • Ein Trait wird innerhalb einer Klasse mit dem Schlüsselwort use eingebunden. Seine Methoden werden dann in die Klasse kopiert, als hätten Sie sie dort selbst geschrieben.
  • Eine Klasse kann beliebig viele Traits verwenden, und ein Trait kann andere Traits verwenden.

Stellen Sie sich einen Trait als compiler-gestütztes Kopieren und Einfügen vor: Der Code des Traits wird wörtlich in jede Klasse eingefügt, die ihn verwendet.

Einen Trait deklarieren und verwenden

Deklarieren Sie einen Trait mit trait und binden Sie ihn dann mit use in eine Klasse ein:

<?php

trait Greetable
{
    public function greet(): string
    {
        return "Hello, my name is {$this->name}.";
    }
}

class User
{
    public function __construct(public string $name) {}

    use Greetable;
}

$user = new User("Ada");
echo $user->greet();
// Output: Hello, my name is Ada.

Beachten Sie, dass der Trait auf $this->name verweist, ohne diese Eigenschaft selbst zu definieren. Ein Trait läuft im Kontext der Klasse, die ihn verwendet, und kann daher auf Eigenschaften und Methoden zurückgreifen, die von dieser Klasse bereitgestellt werden.

Mehrere Traits verwenden

Eine Klasse kann mehrere Traits gleichzeitig verwenden. Trennen Sie sie durch Kommas oder listen Sie jeden mit einer eigenen use-Anweisung auf:

<?php

trait Loggable
{
    public function log(string $message): string
    {
        return "[LOG] " . $message;
    }
}

trait Jsonable
{
    public function toJson(): string
    {
        return json_encode(get_object_vars($this));
    }
}

class Order
{
    use Loggable, Jsonable;

    public function __construct(public int $id, public float $total) {}
}

$order = new Order(7, 49.99);
echo $order->log("created") . PHP_EOL;
echo $order->toJson();
// Output:
// [LOG] created
// {"id":7,"total":49.99}

Konflikte mit insteadof und as auflösen

Wenn zwei Traits eine Methode mit demselben Namen definieren, löst PHP einen fatalen Fehler aus, es sei denn, Sie geben an, welche bevorzugt werden soll. Verwenden Sie insteadof, um einen Gewinner auszuwählen, und as, um die unterlegene Methode unter einem Alias verfügbar zu halten:

<?php

trait FileStorage
{
    public function save(): string
    {
        return "Saved to a file.";
    }
}

trait DatabaseStorage
{
    public function save(): string
    {
        return "Saved to the database.";
    }
}

class Report
{
    use FileStorage, DatabaseStorage {
        DatabaseStorage::save insteadof FileStorage;
        FileStorage::save as saveToFile;
    }
}

$report = new Report();
echo $report->save() . PHP_EOL;       // DatabaseStorage wins
echo $report->saveToFile();           // still reachable via the alias
// Output:
// Saved to the database.
// Saved to a file.

Das Schlüsselwort as kann auch die Sichtbarkeit einer Methode ändern, zum Beispiel protected reset as private — praktisch, wenn eine Trait-Methode intern verfügbar sein soll, aber nicht zur öffentlichen API gehören soll.

Abstrakte und statische Member

Traits können mehr als nur konkrete Instanzmethoden enthalten.

  • Abstrakte Methoden ermöglichen es einem Trait, von der verwendenden Klasse die Implementierung von etwas zu verlangen. So deklariert ein Trait eine Abhängigkeit von seiner Hostklasse.
  • Statische Methoden und Eigenschaften verhalten sich wie normale statische Member, aber jede verwendende Klasse erhält eine eigene Kopie jeder statischen Eigenschaft.
<?php

trait Counter
{
    private static int $count = 0;

    public static function increment(): int
    {
        return ++self::$count;
    }

    // The using class MUST provide this:
    abstract public function label(): string;
}

class PageView
{
    use Counter;

    public function label(): string
    {
        return "views";
    }
}

echo PageView::increment() . PHP_EOL; // 1
echo PageView::increment() . PHP_EOL; // 2
echo (new PageView())->label();       // views
// Output:
// 1
// 2
// views

Traits vs. Vererbung und Interfaces

Wählen Sie das richtige Werkzeug:

  • Vererbung (extends) modelliert eine „ist-ein"-Beziehung und gibt Ihnen eine einzige Elternklasse. Verwenden Sie sie, wenn Klassen tatsächlich eine Typhierarchie teilen. Siehe PHP Vererbung.
  • Interfaces definieren einen Vertrag — welche Methoden existieren — enthalten aber keine Implementierung. Siehe PHP Interfaces.
  • Traits liefern Implementierungen, die Sie in nicht verwandte Klassen einmischen können. Ein gängiges Muster ist ein Interface zusammen mit einem Trait: Das Interface bewirbt die Fähigkeit, der Trait liefert den Standardcode.

Wenn Sie mit diesen Konzepten noch nicht vertraut sind, beginnen Sie mit PHP Klassen und Objekte und PHP Abstrakte Klassen.

Häufige Fallstricke

  • Namenskollisionen sind still, bis sie es nicht mehr sind. Das Kombinieren von Traits, die dieselbe Methode definieren, ist ein fataler Fehler; lösen Sie dies immer mit insteadof/as.
  • Traits werden eingebettet, nicht vererbt. Eine direkt in der Klasse definierte Methode überschreibt die Version des Traits, und die Version des Traits überschreibt alles, was von einer Elternklasse geerbt wird.
  • Statische Eigenschaften sind klassenspezifisch. Zwei Klassen, die denselben Trait verwenden, teilen nicht dessen statischen Zustand — jede erhält eine separate Kopie.
  • Übertreiben Sie es nicht. Ein Trait, der viele Eigenschaften von seiner Hostklasse benötigt, ist oft ein Zeichen dafür, dass Komposition (ein echtes Objekt) oder Vererbung besser passt.

Fazit

Traits geben PHP eine saubere Möglichkeit, Methodenimplementierungen über nicht verwandte Klassen hinweg zu teilen und dabei die Einschränkung der einfachen Vererbung zu umgehen. Deklarieren Sie sie mit trait, binden Sie sie mit use ein, lösen Sie Konflikte mit insteadof und as, und denken Sie daran, dass der Code des Traits in jede verwendende Klasse eingebettet wird. Mit Bedacht eingesetzt halten Traits querschnittliches Verhalten — Logging, Serialisierung, Zähler — an einem Ort, statt es als verstreute Kopien zu hinterlassen.

Übungen

Übung
Was sind die Eigenschaften von PHP Traits?
Was sind die Eigenschaften von PHP Traits?
Was this page helpful?