Dieser Beitrag bringt euch ein weiteres Konzept der objektorientierten Programmierung näher: Die Verwendung von Namensräumen (engl.: namespace).
Ihr solltet bereits wissen was eine Klasse ist, denn bei deren Definition kommt ein Namensraum am häufigsten zum Einsatz. Wollt ihr euer Wissen zu diesem Thema auffrischen, findet ihr hier einen Beitrag dazu: Klassen und Objekte.
Wozu werden Namensräume benötigt?
Bei einem Namensraum (namespace) geht es in erster Linie darum Namenskollisionen bei Klassen und Funktionen zu vermeiden. Insbesondere wenn der eigene Code als Bibliothek anderen zur Verfügung gestellt werden soll, werden besonders gängige Klassennamen (z. B. User, Db, …) schnell zum Problem.
Was ist ein Namensraum in PHP?
Code sagt mehr als 1.000 Worte, also hier erst mal ein Beispiel.
1 2 3 4 5 6 7 |
<?php namespace Oop\Tutorial; class BeispielKlasse { // code } |
Oben seht ihr den vollständigen Inhalt der PHP-Datei BeispielKlasse.php
. Von leeren Zeilen und Kommentaren abgesehen, muss die Definition des Namensraums das erste Element einer PHP-Datei sein.
Von einer Ausnahme abgesehen, auf die wir später noch eingehen, gilt der „namespace“ für die komplette Datei. Da wir uns ohnehin an die Regel „Nur eine Klasse pro Datei“ halten, ist das aber gar nicht so entscheidend.
Definition des Namespace
Definiert wird ein Namensraum mit
1 |
namespace Oop; |
Wir haben die Möglichkeit beliebig tief zu schachteln und damit eine geordnete Struktur aufzubauen. Als Trennzeichen einzelner Namensraumebenen dient in PHP der Backslash (\
).
1 |
namespace Oop\Tutorial\WeitereEbene; |
Die Definition ist immer relativ zum Globalen Namensraum und beginnt daher niemals mit einem Backslash.
Verwendung von Klassen innerhalb eines Namensraums
Vollständig qualifizierter Klassenname
Grundsätzlich möglich, ist die Verwendung des vollständigen Namensraums. Sie beginnt mit einem Backslash.
1 2 3 4 5 6 7 8 9 10 |
namespace Oop\Tutorial; class BeispielKlasse { public function useAnotherClass() { // Mit Verwendung des absoluten / vollstängien Namensraums $var = new \Oop\Tutorial\AndereKlasse(); } } |
Sobald wir uns innerhalb eines Namespaces befinden, müssen wir auch für die Klassen des PHP-Cores mit einem Backslash am Anfangen klar stellen, dass es sich um ein Element aus dem globalen Namespace handelt.
1 2 3 4 5 6 7 8 9 10 11 12 |
namespace Oop\Tutorial; class BeispielKlasse { public function useAnotherClass() { // Ein Element aus dem globalen Namensraum $date = new \DateTime('now'); // oder throw new \RuntimeException(); } } |
Import in den eigenen Namensraum und Alias
Natürlich wollen wir nicht immer so lange Namen schreiben. Dazu bietet uns PHP die Möglichkeit Elemente eines anderen Namensraums in den eigenen zu importieren. Da in diesem Fall wieder Namenskollisionen auftreten können, kann ein Alias zugewiesen werden.
PHP verwendet hierfür das Schlüsselwort use
. Auch hinter use
wird an dieser Stelle vom globalen Namespace ausgegangen und die Angabe beginnt daher nicht mit einem \
.
Möchten wir eine Umbenennung vornehmen, wird sie mit dem Schlüsselwort as
angehängt. Definieren wir kein Alias wird automatisch der ursprüngliche Klassenname verwendet.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
namespace Oop\Tutorial; // Importieren mit automatischem Alias use Mvc\Service\Mailer; use DateTime; // Importieren mit explizietem Alias use Exception as GlobalException; class BeispielKlasse { public function useAnotherClass() { // Klasse ohne Namensraum und Backslash verwenden $mailer = new Mailer(); $date = new DateTime(); throw new GlobalException(); } } |
Importierte Klassen verwenden wir ganz normal ohne führenden Backslash.
Achtung: Gleichnamige Klassen im aktuellen Namensraum wie ein auf diese Art definiertes Alias führen zu Kollisionen – auch dann, wenn sie in der aktuellen Datei gar nicht verwendet werden.
Relativer Klassenname
Möchten wir eine Klasse innerhalb des eigenen Namespace verwenden, können wir dies einfach mit dem Klassennamen selbst.
1 2 3 4 5 6 7 8 9 |
namespace Oop\Tutorial; class BeispielKlasse { public function useAnotherClass() { $ding = new AndereKlasse(); } } |
Nachgelagerte Namensräume, also Unterstrukturen des aktuellen, können wir relativ ansteuern.
1 2 3 4 5 6 7 8 9 10 |
namespace Oop\Tutorial; class BeispielKlasse { public function useAnotherClass() { // vollständiger Klassenname wäre: \Oop\Tutorial\Utils\AndereKlasse() $ding = new Utils\AndereKlasse(); } } |
Empfohlene Dateistruktur – Best Practices
Analog zur Benennung unserer Dateien nach dem Klassennamen inklusive Groß- und Kleinschreibung, sollten wir unsere Ordnerstruktur anhand der Namensräume festlegen. Im obigen Beispiel befindet sich also die Datei BeispielKlasse.php
im Ordner .../Oop/Tutorial/
.
1 2 3 4 5 6 7 8 9 |
Projektordner/ |- app/ |- config/ |- src/ |- Controller/ |- Component/ |- Oop/ |- Tutorial/ |- BeispielKlasse.php |
Mit dieser Struktur folgt ihr dem PSR-4 Schema und erspart euch später beim Autoloading eine Menge Arbeit.
Es hat sich eingebürgert als obersten Namensraum den eigenen Firmennamen oder ein Pseudonym zu verwenden, so dass es keine eigenen Klassen im globalen Gültigkeitsbereich gibt.
Magische Konstanten
Im Zusammenhang mit Namensräumen helfen uns auch die magischen Konstanten __NAMESPACE__
und __CLASS__
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
namespace Oop\Tutorial; class BeispielKlasse { public function useAnotherClass() { echo __NAMESPACE__; /* Ausgabe: Oop\Tutorial */ echo __CLASS__; /* Ausgabe: Oop\Tutorial\BeispielKlasse */ } } |
__NAMESPACE__
liefert uns einen String. Wollen wir damit einen Klassennamen erzeugen sieht das so aus:
1 2 3 4 5 |
public function classFromNamespace() { $className = __NAMESPACE__.'\\Utils\\AndereKlasse'; $class = new $className(); } |
Dies kann mit dem Schlüsselwort namespace
vereinfacht werden.
1 2 3 4 |
public function classFromNamespace() { $class = new namespace\Utils\AndereKlasse(); } |
Mehrere Namensräume in einer Datei
Auch wenn üblicherweise nur ein Namensraum pro Datei verwendet werden sollte, bietet PHP dennoch die Möglichkeit mehrere zu definieren. Dazu muss der gesamte Inhalt mit geschweiften Klammern umgeben werden.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?php namespace Math { function add($a, $b) { return $a + $b; } } namespace String { function add($a, $b) { return $a . $b; } } |
Wie ihr sehen könnt, dürfen die Funktionen den selben Namen haben.
Funktionen mit Namensräumen
Wie wir gerade gesehen haben, können auch Funktionen innerhalb eines Namensraums definiert werden. Bei der Verwendung gelten dann die selben Regeln wie bei Klassen.
1 2 3 4 5 |
require_once 'functions.php'; echo \Math\add(5, 3); // 8 echo \String\add(5, 3); // 53 |
Weitere Tutorials zum Thema objektorientierte Programmierung