Hallo und willkommen zum zweiten Posting bezüglich der neuen PHP 5.4 Features. Heute möchte ich mich etwas genauer mit den „Traits“ auseinander setzen. Traits sollen uns helfen Code wiederzuverwenden – Bisher ist es in der Objekt orientierten Programmierung mit PHP nur möglich Methoden von übergeordneten Klassen zu erben. Das „Problem“ an der Sache: Eine Klasse kann nur von einer anderen Klasse erben. Hier sollen die Traits Abhilfe schaffen!

Mit Traits ist es möglich eine „Methodensammlung“ an beliebig viele PHP Klassen zu übergeben – Es ist zudem möglich innerhalb einer Klasse beliebig viele Traits zu nutzen.

Natürlich gibt es auch Heute wieder in paar Code-Beispiele damit ihr euch etwas genauer vorstellen könnt wie sowas aussieht! Die Beispiele basieren auf der Dokumentation von php.net, denn es fällt mir ehrlich gesagt noch etwas schwer wirklich sinnvolle und einfache Beispiele für Traits zu finden 😉

Also legen wir mal los – Die Syntax der Traits dürfte uns bekannt vorkommen:

// Beispielhafte Syntax für ein Trait in PHP.
trait hallo_welt
{
    public function sag_hallo() 
    {
        echo 'Hallo Welt!';
    }
}

Wir sehen, Traits sehen aus wie Klassen. Hier müssen wir uns also (Bis auf „trait“ statt „class“) nicht umgewöhnen, super!
Die Methoden eines Traits lassen sich, wie die von normalen Klassen, als private, protected und public deklarieren. Auch static und abstract funktioniert, wie wir es von Klassen gewohnt sind!

Aber wie werden Traits in eine Klasse eingebunden und genutzt? Das geht ganz einfach:

// Beispielhafte Syntax für das einbinden von Traits.
trait hallo_planet
{
    public function sag_hallo() 
    {
        /*
         * Sollten die benutzenden Klassen keine Variable "planet" definieren
         * wird ein PHP Notice geworfen. Die fehlende Variable wird wie NULL behandelt.
         */
        return 'Hallo ' . $this->planet . '!';
    }
}

class hallo_welt
{
    use hallo_planet;

    private $planet = 'Welt';
}

class hallo_mars
{
    use hallo_planet;

    private $planet = 'Mars';
}

echo (new hallo_welt)->sag_hallo() . '<br />';
echo (new hallo_mars)->sag_hallo();

Dieser Code-Schnipsel wird folgende Ausgabe liefern:

Hallo Welt!
Hallo Mars!

Gar nicht so schwierig, oder? Aber das ist ja auch noch ein relativ überschaubares Beispiel. Es geht mit einer Klasse weiter, die mehrere Traits nutzt:

// Mehrere Traits in einer Klasse.
trait hallo
{
    public function sag_hallo() 
    {
        return 'Hallo ';
    }
}

trait welt
{
    public function sag_welt() 
    {
        return 'Welt!';
    }
}

class hallo_welt
{
    use hallo, welt;
}

$hallo_welt = new hallo_welt;

echo $hallo_welt->sag_hallo() . $hallo_welt->sag_welt();

Das Ergebnis dieses Beispieles sollte euch nicht sehr überraschen…

Hallo Welt!

Nun wird es aber etwas spannender – Was geschieht, wenn wir zwei Traits einbinden die ein- oder mehrere gleich benannte Methoden besitzen? In diesem Fall müssen wir mit Hilfe von insteadof („anstelle von“) definieren welche Methode welches Traits den Vorrang hat (Tun wir dies nicht, wird PHP es mit Fatal Errors bestrafen):

// Zwei Traits mit identischen Methoden.
trait A
{
    public function sprich() 
    {
        return 'Ich bin A!';
    }
}

trait B
{
    public function sprich() 
    {
        return 'Ich bin B';
    }
}

class Meine_Klasse
{
    use A, B {
        A::sprich insteadof B;
    }
}

// ...

Mit dieser neuen (und noch etwas ungewohnten) Syntax können wir also definieren welches Trait den Vorzug gegenüber den anderen hat. Falls wir mehr als zwei Traits benutzen, müssen die anderen Traits ebenfalls aufgeführt werden:

/*
 * Wir gehen von definierten Traits A, B, C und D aus
 * welche jeweils die Methode "sprich" implementieren.
 */

class Meine_Klasse
{
    use A, B, C, D {
        A::sprich insteadof B;
        A::sprich insteadof C;
        A::sprich insteadof D;
    }
}

// ...

Doch was, wenn wir die „sprich“ Methode von Trait A und Trait C benutzen wollen? Hier bietet uns PHP die Möglichkeit einen Alias zu setzen. Das ganze sieht dann etwa so aus:

/*
 * Wir gehen von definierten Traits A, B, C und D aus
 * welche jeweils die Methode "sprich" implementieren.
 */

class Meine_Klasse
{
    use A, B, C, D {
        A::sprich insteadof B;
        A::sprich insteadof C;
        A::sprich insteadof D;
        C::sprich as sprich_c;
    }
}

$meine_klasse = new Meine_Klasse;
echo $meine_klasse->sprich() . '<br />';
echo $meine_klasse->sprich_c();

Führen wir diesen Quelltext aus, so erhalten wir die folgende Ausgabe:

Ich bin A
Ich bin C

Aber natürlich geht es noch weiter!

Steigen wir noch etwas tiefer in die Materie ein… Mit hilfe des as Operator ist es nämlich außerdem möglich die Sichtbarkeit der Methoden eines Traits zu verändern! So können wir eine private Methode eines Traits in einer Klasse als public deklarieren und umgekehrt (das gleiche gilt für protected).
Natürlich auch hierzu ein Code Beispiel:

// Mehrere Traits mit identischen Methoden.
trait A
{
    public function sprich() 
    {
        return 'Ich bin A!';
    }
}

trait B
{
    private function sprich() 
    {
        return 'Ich bin B';
    }
}

class Meine_Klasse
{
    use A, B {
        A::sprich insteadof B;
        B::sprich as public sprich_b;
    }
}

Obwohl die Methode „sprich“ des Traits B als privat deklariert ist, können wir in einer Instanz der Klasse „Meine_Klasse“ darauf zugreifen, da wir sie in diesem Kontext als public deklariert haben. Das ist ein wirklich geniales Feature, was aber unbedingt mit Vorsicht zu genießen ist!

Wir sind übrigens beinahe fertig mit dem Thema Traits! Aber eine Sache gibt es noch, die ich unbedingt aufzeigen möchte:

Traits können (genau wie Klassen) Traits einbinden

// Traits können andere Traits einbinden.
trait Erde
{
    public function sag_planet() 
    {
        return 'Erde';
    }
}

trait Hallo_Planet
{
    use Erde;

    public function sprich() 
    {
        return 'Hallo ' . $this->sag_planet();
    }
}

class Sag_Hallo
{
    use Hallo_Planet;
}

echo (new Sag_Hallo)->sprich();

Liefert die folgende Ausgabe…

Hallo Erde

Und damit dürfte dann eigentlich jeder Wunsch erfüllt sein 😉 Traits werden uns in Zukunft sicherlich häufig über den Weg laufen – Ich hoffe der Eintrag hat euch etwas auf diese erste Begegnung vorbereitet! Wie beim letzten Posting zu den PHP 5.4 Themen möchte ich jeden dazu auffordern sich selbst ein Bild der neuen Features zu machen – Spielt etwas mit den neuen Möglichkeiten und testet verschiedenes aus!
Einige interessante Effekte lassen sich bereits jetzt in den Kommentaren der Dokumentation auf php.net finden…

Ich hoffe ihr habt nun einen guten Einblick in das Thema – Sollte euch etwas fehlen, zögert bitte nicht dies zu äußern!

Mit diesen abschließenden Worten möchte ich euch allen eine angenehme Woche wünschen!

3 comments on “PHP 5.4.0: Traits

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.