PHP Namespaces verstehen
PHP Namespaces kapseln Klassen, Funktionen und Konstanten in einer logischen Struktur und verhindern so Namenskonflikte.
Ein Namespace ist eine Möglichkeit, verwandte Klassen, Interfaces, Funktionen und Konstanten unter einem einzigen logischen Namen zu gruppieren. Stellen Sie sich das wie Ordner auf Ihrer Festplatte vor: Zwei Dateien namens index.php können nebeneinander existieren, solange sie sich in unterschiedlichen Ordnern befinden. Auf die gleiche Weise können zwei Klassen namens User nebeneinander existieren, solange sie sich in unterschiedlichen Namespaces befinden.
Ohne Namespaces lebt jeder Name in einem gemeinsamen globalen Raum. Sobald Ihr Projekt und eine Drittanbieter-Bibliothek beide eine Klasse namens Logger oder Router definieren, wirft PHP einen fatalen Cannot declare class-Fehler. Namespaces sind die Standardlösung für dieses Problem und bilden die Grundlage des Composer-Autoloadings (dem PSR-4-Standard), das von praktisch jedem modernen PHP-Paket verwendet wird.
Warum Namespaces wichtig sind
- Namenskonflikte vermeiden. Zwei Codeabschnitte können denselben Klassen- oder Funktionsnamen verwenden, solange sie in unterschiedlichen Namespaces liegen, sodass Ihr Code nie mit dem Code einer Bibliothek kollidiert.
- Große Codebasen organisieren. Namespaces spiegeln Ihre Ordnerstruktur wider und machen es deutlich, wo eine Klasse zu finden ist (
App\Service\Mailerbefindet sich inapp/Service/Mailer.php). - Autoloading ermöglichen. PSR-4 ordnet ein Namespace-Präfix einem Verzeichnis zu, sodass Composer Klassen bei Bedarf laden kann, ohne manuelle
require-Anweisungen.
Einen Namespace definieren
Deklarieren Sie einen Namespace mit dem Schlüsselwort namespace. Es muss die allererste Anweisung in der Datei sein (nur eine declare(strict_types=1)-Zeile darf davor stehen). Namespace-Namen verwenden einen Backslash (\) zur Trennung der Ebenen:
<?php
namespace App\Service;
class Mailer
{
public function send(string $to): string
{
return "Mail sent to {$to}";
}
}Alles, was unterhalb der namespace-Zeile deklariert wird — die Klasse Mailer — lebt nun in App\Service. Ihr vollständig qualifizierter Name lautet App\Service\Mailer.
Auf Namespace-Elemente verweisen
Es gibt drei Möglichkeiten, auf einen Namen aus einem anderen Namespace zu verweisen.
Ein vollständig qualifizierter Name beginnt mit einem führenden \ und benennt das Element vom globalen Wurzelverzeichnis aus — er ist überall eindeutig:
<?php
$mailer = new \App\Service\Mailer();Ein qualifizierter Name ist relativ zum aktuellen Namespace, sodass Service\Mailer innerhalb des Namespace App zu App\Service\Mailer aufgelöst wird. Ein Name ohne Backslash ist unqualifiziert und wird zunächst im aktuellen Namespace gesucht.
Importieren mit use
Den vollständigen Pfad jedes Mal zu schreiben ist umständlich. Das Schlüsselwort use importiert einen Namen in die aktuelle Datei, sodass Sie ihn mit seinem Kurznamen referenzieren können:
<?php
use App\Service\Mailer;
$mailer = new Mailer(); // resolves to App\Service\MailerAlias mit as
Wenn zwei importierte Klassen denselben Kurznamen haben, geben Sie einer einen Alias mit as, um sie zu unterscheiden:
<?php
use App\Service\Mailer;
use Vendor\Mail\Mailer as VendorMailer;
$a = new Mailer(); // App\Service\Mailer
$b = new VendorMailer(); // Vendor\Mail\MailerFunktionen und Konstanten importieren
use importiert standardmäßig Klassen. Um eine Funktion oder eine Konstante zu importieren, fügen Sie das Schlüsselwort function oder const hinzu:
<?php
use function App\Helpers\format_price;
use const App\Config\TAX_RATE;Achtung: Eingebaute Funktionen benötigen einen führenden Backslash
Innerhalb eines Namespace wird ein unqualifizierter Funktions- oder Klassenname zuerst gegen den aktuellen Namespace aufgelöst. Bei eingebauten Funktionen fällt PHP automatisch auf die globale Version zurück, aber bei eingebauten Klassen tut es das nicht. Daher erfordert die Referenzierung von Kernklassen wie Exception, DateTime oder PDO einen führenden Backslash (oder eine use-Anweisung):
<?php
namespace App\Service;
// Wrong: PHP looks for App\Service\Exception, which doesn't exist.
// throw new Exception('boom');
// Right: leading backslash points to the global class.
throw new \Exception('boom');Ein gebräuchliches, lesbares Muster ist es, die Kernklasse am Anfang der Datei zu importieren:
<?php
namespace App\Service;
use Exception;
throw new Exception('boom'); // now the short name worksEin vollständiges Beispiel
Dieses einzelne, ausführbare Skript definiert zwei Namespaces und verwendet beide — es zeigt, wie derselbe Kurzklassenname an zwei Stellen ohne Konflikte existieren kann:
<?php
namespace App\Billing {
class Invoice
{
public function label(): string
{
return 'Billing invoice';
}
}
}
namespace App\Shipping {
class Invoice
{
public function label(): string
{
return 'Shipping invoice';
}
}
}
namespace Main {
use App\Billing\Invoice;
use App\Shipping\Invoice as ShippingInvoice;
$billing = new Invoice();
$shipping = new ShippingInvoice();
echo $billing->label() . "\n";
echo $shipping->label() . "\n";
}Ausgabe:
Billing invoice
Shipping invoiceDie geschweifte
namespace Foo { ... }-Syntax wird nur verwendet, um mehrere Namespaces in einer Datei unterzubringen (praktisch für ein eigenständiges Beispiel). In echten Projekten legt man einen Namespace pro Datei an und verlässt sich auf Composer-Autoloading.
Namespaces und Autoloading (PSR-4)
In der Praxis schreiben Sie die Namespace-Grenzen selten manuell für jede Datei — Composer übernimmt die Verdrahtung. Eine PSR-4-Zuordnung in composer.json verknüpft ein Namespace-Präfix mit einem Ordner:
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}Mit dieser Konfiguration wird die Klasse App\Service\Mailer automatisch aus src/Service/Mailer.php geladen, wenn Sie sie zum ersten Mal referenzieren — ohne manuelles include oder require.
Zusammenfassung
- Ein Namespace gruppiert Klassen, Interfaces, Funktionen und Konstanten, um Namenskonflikte zu verhindern und Code zu organisieren.
- Deklarieren Sie ihn mit
namespace App\Service;als erste Anweisung in der Datei. - Referenzieren Sie Elemente über ihren vollständig qualifizierten Namen (
\App\Service\Mailer) oder, häufiger, importieren Sie sie mituseund verwenden Sie den Kurznamen. - Vergeben Sie Aliase für kollidierende Namen mit
asund denken Sie daran, globale Klassen wie\Exceptioninnerhalb eines Namespace mit einem führenden Backslash zu versehen. - PSR-4-Autoloading ordnet ein Namespace-Präfix einem Verzeichnis zu — so wird jedes Composer-Paket geladen.