0251 / 590 837 15
info@a-coding-project.de

Traits – Mehrfachvererbung in PHP

Habt ihr euch nicht schon mal gewünscht in PHP mehr als nur von einer Klasse zu erben? Die so genannten Traits, die es ab PHP 5.4 gibt, machen es endlich möglich. Bei der „normalen“ Vererbung hat man ja zum Beispiel eine abstrakte Klasse von der man direkt erbt. Ein Trait ist da gar nicht mal so anders. Im Gegensatz zu Klassen kann man Traits nicht instanzieren, also daraus keine Objekte erstellen. Der Vorteil ist, dass man aber eben von mehreren Traits erben kann, was bei abstrakten Klassen so nicht geht.

Als Beispiel nehmen wir mal ein Smartphone, welches natürlich eine ganze Reihe an Funktionen hat. Kümmern wir uns doch als erstes mal um das Anrufen:

<?php
	trait call
	{
		public function call()
		{
			echo "ring ring";
		}
	}
?>

Das ist unser erster Trait! Im Endeffekt so aufgebaut wie eine normale Klasse, nur dass sie hier mit Trait gekennzeichnet ist. Nun müssen wir noch unser Smatphone herstellen. Das sieht bei mir dann so aus:

<?php
	class phone
	{
		use call;
	}
?>

Mit dem use sagt man, welche Traits man verwenden möchte. Jetzt hat die Klasse phone automatisch die Funktion call bekommen:

<?php
	include("call.php");
	include("phone.php");
	
	$phone = new phone();
	$phone->call();
?>

Mehrfachvererbung

Fügen wir doch unserem Smartphone eine weitere Funktion hinzu. Wie wäre es mit einem Radio?

<?php
	trait radio
	{
		public function play()
		{
			echo "lalala";
		}
	}
?>

Im use unserer phone-Klasse können wir nun per Komma einfach einen weiteren Trait zuweisen:

<?php
	class phone
	{
		use call, radio;
	}
?>

Die Funktion play kann somit ebenfalls aufgerufen werden.

Methoden umbenennen

Leider sagt uns „play“ noch nicht wirklich, dass es sich ums Radio handelt. Benennen wir diese doch einfach in play_radio um:

<?php
	class phone
	{
		use call, radio {
			radio::play as play_radio;
		}
	}
?>

In den geschweiften Klammern hinter dem use kann man mittels as angeben, wie eine funktion umbenennt werden soll. Wobei „Umbenennen“ nicht komplett zutrifft, da die Funktion jetzt unter „play“ und „play_radio“ aufgerufen werden kann.

Sichtbarkeiten von Funktionen ändern

Sagen wir mal, der Benutzer hat für sein Smartphone noch eine Prepaid-Abrechnung. In dem Fall darf die Call-Funktion ja nicht ohne Weiteres aufgerufen werden. Wir müssen ja vorerst sein Guthaben prüfen. Der einfache Weg wäre jetzt, die call-Funktion zu überschreiben und mittels parent::call() darin die Ursprungsfunktions aufzurufen. Das ist hier aber nicht möglich, da es sich um keine direkte Vererbung handelt. Aber natürlich gibt es auch hier eine elegante Lösung:

<?php
	class phone
	{
		private $money = 10;
		
		use call, radio {
			call::call as private;
			radio::play as play_radio;
		}
		
		public function call_prepaid()
		{
			if($this->money > 0)
			{
				$this->call();
			}
		}
	}
?>

Hier wird die Funktion call als private deklariert und kann damit nicht mehr von außen aufgerufen. Mit call_prepaird wird dann das Guthaben geprüft und im Erfolgsfall der Anruf getätigt. Man kann der Funktion call aber auch wie bei play_radio einen alias mitgeben und die endgültige Funktion dann nur call nennen.

Konflikte beachten

Fügen wir doch noch den Trait prepaid_card hinzu:

<?php
	trait prepaid_card
	{
		public $money = 10;
	}
?>

Diese legen wir noch ins Smartphone:

<?php
	class phone
	{
		private $money = 10;
		
		use call, radio, prepaid_card {
			call::call as private;
			radio::play as play_radio;
		}
		
		public function call_prepaid()
		{
			if($this->money > 0)
			{
				$this->call();
			}
		}
	}
?>

Und was kommt heraus? Ein PHP-Fehler!


Fatal error: phone and prepaid_card define the same property ($money) in the composition of phone. However, the definition differs and is considered incompatible. Class was composed in traitsphone.php on line 18

Der Grund ist, dass die Traits im Gegensatz zu abstrakten Klassen nicht in einen eigenen Kontext laufen. Dass heißt auch private Variablen sind in allen Klassen, die die Traits benutzen verfügbar. Hier sollte man die Variablen vernünftig benennen. Man sollte auch in Betracht ziehen, vielleicht doch eine abstrakte Klasse zu nutzen, eine kann man ja einbauen.

Weitere Funktionen

Traits bieten noch ein paar weitere Funktionen. So kann man auch statische oder abstrakte Funktionen definieren. Schaut doch einfach mal in die PHP Doku zu Traits.

Kommentare

Tutorial: Erste Schritte mit Facebooks &#8220;Hack&#8221; schrieb am 24.03.2014:

[&#8230;] Traits ist es möglich anzugeben, dass diese nur in bestimmten Unterklassen genutzt werden können. Das [&#8230;]