insteadof
Das Schlüsselwort "insteadof" wird in PHP verwendet, um bei Methodenkonflikten zwischen Traits anzugeben, welcher Trait bevorzugt wird.
Das PHP-Schlüsselwort insteadof
insteadof ist ein PHP-Schlüsselwort, das innerhalb eines use-Blocks verwendet wird, um Methodenkonflikte zwischen zwei oder mehr Traits aufzulösen. Wenn eine Klasse mehrere Traits verwendet, die jeweils eine Methode mit demselben Namen definieren, kann PHP nicht entscheiden, welche Methode gilt, und wirft einen schwerwiegenden Fehler. insteadof teilt PHP mit, welche Methode des Traits beibehalten und welche verworfen werden soll.
Diese Seite erläutert das Problem, das insteadof löst, die genaue Syntax, das Zusammenspiel mit dem Schlüsselwort as, um die "verlierende" Methode zu behalten, sowie wichtige Fallstricke.
Das Problem, das insteadof löst
Ein Trait ist ein wiederverwendbarer Block von Methoden, der zur Kompilierzeit in eine Klasse kopiert wird. Wenn zwei Traits eine Methode mit demselben Namen definieren und eine Klasse beide verwendet, kann PHP keine Wahl treffen. Das Ergebnis ist ein schwerwiegender Kollisionsfehler:
<?php
trait FileLogger {
function log() { echo "Writing to a file."; }
}
trait DatabaseLogger {
function log() { echo "Writing to the database."; }
}
class Service {
use FileLogger, DatabaseLogger; // No conflict resolution
}
// PHP Fatal error: Trait method DatabaseLogger::log has not been
// applied as Service::log, because of collision with FileLogger::logDie Klasse wird nie erstellt — dies ist ein Kompilierfehler, kein Laufzeitfehler. Mit insteadof teilt man PHP mit, welche Methode angewendet werden soll.
Syntax
insteadof wird innerhalb des geschweiften Klammerblocks einer use-Anweisung geschrieben:
use TraitWeKeep, TraitWeDrop {
TraitWeKeep::methodName insteadof TraitWeDrop;
}Zu lesen als: "Verwende TraitWeKeeps methodName anstatt TraitWeDrops." Dem Trait, der nach insteadof genannt wird, wird seine Version dieser Methode aus der Klasse entfernt.
<?php
trait FileLogger {
function log() { echo "Writing to a file."; }
}
trait DatabaseLogger {
function log() { echo "Writing to the database."; }
}
class Service {
use FileLogger, DatabaseLogger {
FileLogger::log insteadof DatabaseLogger;
}
}
$service = new Service();
$service->log();
// Output: Writing to a file.Auflösung bei mehr als zwei Traits
Wenn drei oder mehr Traits kollidieren, werden alle auszuschließenden Traits nach insteadof durch Kommas getrennt aufgelistet:
<?php
trait Json { function format() { echo "JSON output"; } }
trait Xml { function format() { echo "XML output"; } }
trait Csv { function format() { echo "CSV output"; } }
class Report {
use Json, Xml, Csv {
Json::format insteadof Xml, Csv;
}
}
$report = new Report();
$report->format();
// Output: JSON outputDie andere Methode mit as behalten
insteadof verwirft eine Methode — doch häufig möchte man sie nicht verlieren, sondern sie lediglich unter einem anderen Namen verfügbar machen, damit beide zugänglich sind. Genau das bewirkt das Schlüsselwort as: Es erstellt einen Alias für die ausgeschlossene Methode unter einem neuen Namen, sodass sie die Konfliktauflösung übersteht.
<?php
trait FileLogger {
function log($msg) { return "[file] $msg"; }
}
trait DatabaseLogger {
function log($msg) { return "[db] $msg"; }
}
class Service {
use FileLogger, DatabaseLogger {
FileLogger::log insteadof DatabaseLogger; // FileLogger::log becomes log()
DatabaseLogger::log as logToDb; // DatabaseLogger::log survives as logToDb()
}
}
$service = new Service();
echo $service->log("started"), "\n"; // [file] started
echo $service->logToDb("started"), "\n"; // [db] startedinsteadof und as werden fast immer gemeinsam verwendet: insteadof bestimmt den Gewinner, as rettet den Verlierer unter einem Alias. Das Schlüsselwort as kann außerdem die Sichtbarkeit einer Methode ändern (zum Beispiel FileLogger::log as protected;).
Fallstricke
insteadoflöst nur Namenskollisionen. Wenn die beiden Methoden keinen gemeinsamen Namen haben, gibt es keinen Konflikt undinsteadofist nicht notwendig.- Es muss auf eine Methode verwiesen werden, die im genannten Trait tatsächlich existiert, unter Verwendung der Syntax
Trait::method. Ein Tippfehler führt zu einem schwerwiegenden Fehler. - Es wird kein Verhalten zusammengeführt. Die ausgeschlossene Methode wird schlicht nicht angewendet;
insteadofruft niemals beide Methoden auf. - Eine Kindklasse hat weiterhin Vorrang vor einem Trait. Die Methodenauflösungsreihenfolge lautet: In der Klasse selbst definierte Methoden überschreiben Trait-Methoden, und Trait-Methoden überschreiben geerbte (Elternklassen-)Methoden.
Wann man es verwenden sollte
insteadof kommt dann zum Einsatz, wenn eine Klasse aus mehreren Traits zusammengesetzt wird und zwei davon zufällig denselben Methodennamen verwenden — was häufig vorkommt, wenn Drittanbieter-Traits gemischt werden, über die man keine Kontrolle hat. In Kombination mit as ermöglicht es, Verhalten aus unabhängigen Traits zu kombinieren, ohne deren Quellcode umbenennen zu müssen, und hält Klassen modular und konfliktfrei.