Abschnitt 9 · Lektion 5
Eigener Code mit Autoloading
Bis hierher hattest du eine Datei, und wenn du eine zweite brauchtest, hast du sie mit require
eingebunden. Bei fünf Dateien geht das noch. Bei fünfzig nicht mehr, und spätestens dann bindest du
irgendwo dieselbe Datei zweimal ein und bekommst „Cannot redeclare”.
Namensräume, so weit man sie hier braucht
Ein Namensraum ist ein Vorname für deine Klassen. Steht am Anfang einer Datei die Zeile
namespace App\Modell;, dann heißt die Klasse Notiz in dieser Datei mit vollem Namen
App\Modell\Notiz. Der Rückstrich trennt dabei die Teile, so wie der Schrägstrich Ordner trennt.
Das löst ein Problem, das du im Kurs bisher nicht hattest, in einem Projekt mit Paketen aber sofort
bekommst: Zwei Klassen dürfen Notiz heißen, deine und die aus einem Paket, solange ihre
Namensräume verschieden sind. Ohne Namensräume gäbe es eine Notiz im ganzen Programm, und wer sie
zuerst einbindet, gewinnt.
Mit use App\Modell\Notiz; oben in einer Datei sagst du: Wenn ich hier unten Notiz schreibe,
meine ich diese. Alles Weitere zu Namensräumen steht in Lektion 13.5. Für diese Lektion reichen die
zwei Zeilen.
Autoloading ist eine Zeichenkettenersetzung
<?php
namespace App\Modell;
class Notiz
{
public string $text = "Milch kaufen";
} <?php
// spl_autoload_register meldet eine Funktion an, die PHP genau dann
// fragt, wenn es eine Klasse noch nicht kennt. Mehr steckt hinter
// Autoloading nicht.
spl_autoload_register(function (string $klasse): void {
$praefix = "App\\";
if (!str_starts_with($klasse, $praefix)) {
return;
}
$rest = substr($klasse, strlen($praefix));
$datei = "src/" . str_replace("\\", "/", $rest) . ".php";
echo " gefragt nach: ", $klasse, "\n";
echo " gesucht in: ", $datei, "\n";
if (is_file(__DIR__ . "/" . $datei)) {
require __DIR__ . "/" . $datei;
}
});
echo "Vor der ersten Benutzung ist noch nichts geladen.\n";
$notiz = new App\Modell\Notiz();
echo "Geladen: ", get_debug_type($notiz), "\n";
echo "Inhalt: ", $notiz->text, "\n"; Hier steht kein Composer, sondern ein Autolader von Hand, und ohne die zwei echo-Zeilen, die es
nur zum Zusehen gibt, sind es vierzehn Zeilen. spl_autoload_register() meldet eine Funktion an, und
PHP ruft sie genau dann auf, wenn eine Klasse gebraucht wird, die es noch nicht kennt. Die Funktion bekommt den vollen Klassennamen und hat eine einzige Aufgabe: die
passende Datei einbinden.
Der Weg vom Namen zum Pfad ist die ganze Regel:
- Fängt der Klassenname mit
App\an? Wenn nicht, ist diese Funktion nicht zuständig. - Präfix abschneiden, aus jedem Rückstrich einen Schrägstrich machen,
.phpanhängen. - Vorn den Ordner davor, der zum Präfix gehört.
Aus App\Modell\Notiz wird so src/Modell/Notiz.php. Genau das ist PSR-4, und das steckt
hinter dem Kürzel: eine Absprache darüber, wie ein Klassenname in einen Dateipfad übersetzt wird.
Sie ist so einfach, weil sie einfach sein muss: Alle Pakete halten sich daran, und ein einziger
Autolader kann deshalb für alle zuständig sein.
Sieh dir die Reihenfolge in der Ausgabe an. Die erste Zeile steht da, bevor der Autolader überhaupt
gefragt wurde. Er wird erst beim new gerufen, und nur dann. Wer zwanzig Klassen im Projekt hat und
in einem Aufruf zwei davon benutzt, lädt zwei Dateien.
Im Reiter Debug siehst du diesen Sprung als Weg durch die Datei: Nach der Zeile mit new geht
es nach oben in die Funktion, klasse steht mit dem vollen Namen da, dann kommen rest und
datei dazu, und nach dem require geht es unten mit der Zeile Geladen: weiter. Das ist die
ganze Übersetzung, Schritt für Schritt, an dem Namen, den PHP gerade gefragt hat.
Und warum die Groß- und Kleinschreibung zählt
<?php
// Dieselbe Klasse, nur heisst die Datei jetzt notiz.php statt
// Notiz.php. Auf einem Windows-Rechner faellt das nie auf.
namespace App\Modell;
class Notiz
{
public string $text = "Milch kaufen";
} <?php
spl_autoload_register(function (string $klasse): void {
$praefix = "App\\";
if (!str_starts_with($klasse, $praefix)) {
return;
}
$rest = substr($klasse, strlen($praefix));
$datei = "src/" . str_replace("\\", "/", $rest) . ".php";
echo " gesucht in: ", $datei, "\n";
if (is_file(__DIR__ . "/" . $datei)) {
require __DIR__ . "/" . $datei;
}
});
$notiz = new App\Modell\Notiz();
echo "Hierher kommt das Programm nicht mehr.\n"; Dieselbe Klasse, dieselbe Regel, nur heißt die Datei notiz.php statt Notiz.php. Der Autolader
sucht nach src/Modell/Notiz.php, findet nichts, und PHP bricht mit
Uncaught Error: Class "App\Modell\Notiz" not found ab; php index.php; echo $? im Terminal zeigt
die 255 darunter. Dass es wirklich nur am Namen liegt, beweist ein einziger Befehl:
mv src/Modell/notiz.php src/Modell/Notiz.php, dann noch einmal php index.php, und das Programm
läuft bis zur letzten Zeile durch. Der nächste Prüflauf legt die Datei wieder klein geschrieben hin,
so wie sie im Editor steht.
Auf einem Windows-Rechner läuft dasselbe Projekt, weil dort notiz.php und Notiz.php dieselbe
Datei sind. Auf einem Linux-Server nicht, und das ist der Klassiker unter den Fehlern, die erst
beim Hochladen auftauchen. Die Regel dagegen ist einfach: Die Datei heißt genau wie die Klasse
darin, und der Ordner genau wie der Namensraum.
Dieselbe Arbeit, von Composer erledigt
{
"name": "hofladen/notizen",
"require": {
"php": "^8.2",
"ramsey/uuid": "^4"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
} <?php
namespace App\Modell;
use Ramsey\Uuid\Uuid;
class Notiz
{
public string $text;
public string $kennung;
public function __construct(string $text)
{
$this->text = $text;
$this->kennung = Uuid::uuid4()->toString();
}
} <?php
// Eine einzige Zeile, und danach ist beides da: das fremde Paket
// und der eigene Code aus src/.
require __DIR__ . "/vendor/autoload.php";
$notiz = new App\Modell\Notiz("Milch kaufen");
echo "Klasse: ", get_debug_type($notiz), "\n";
echo "Text: ", $notiz->text, "\n";
echo "Kennung: ", $notiz->kennung, "\n"; In der Praxis schreibt niemand den Autolader von Hand. Man trägt die Regel in die composer.json
ein, in einen Block autoload, darin einen Block psr-4, und darin steht die Zuordnung selbst:
links das Präfix "App\\", rechts der Ordner "src/". Im Beispiel oben kannst du sie in der
composer.json nachlesen.
Die zwei Rückstriche hinter App sind eine Eigenheit von JSON, nicht von PHP: Ein Rückstrich leitet
dort Sonderzeichen ein, ein echter muss deshalb verdoppelt werden. Gemeint ist einer.
Danach lädt vendor/autoload.php beides: die Pakete und deinen eigenen Code. Im Beispiel benutzt
die eigene Klasse App\Modell\Notiz das fremde ramsey/uuid, und in index.php steht trotzdem nur
eine einzige require-Zeile.
Wenn du den Block nachträglich einträgst, muss Composer die Autoload-Dateien einmal neu erzeugen.
Der Befehl dafür heißt composer dump-autoload. In diesem Kurs fährt ihn die Plattform bei jedem
Lauf selbst, gleich nachdem sie das Paket ausgepackt hat, deshalb musst du ihn hier nie tippen.
Kannst du aber, er braucht kein Netz: Er meldet Generating autoload files, und
cat vendor/composer/autoload_psr4.php zeigt danach, was dabei entstanden ist. Ganz unten steht
deine Zuordnung, 'App\\' => array($baseDir . '/src'), über ihr die drei Zeilen der Pakete. Nimm
den Block aus der composer.json heraus und tipp php index.php: Class "App\Modell\Notiz" not found, dieselbe Meldung wie beim falschen Dateinamen, diesmal weil Composer den Ordner src/ gar
nicht kennt. Das Paket daneben lädt weiter, nur dein eigener Code nicht mehr.
Die Klassen im Beispiel kennst du aus Lektion 6.7, neu sind hier nur die zwei Zeilen darüber: der
Namensraum und die Einfuhr. Worauf es ankommt, ist die Frage, ob der Pfad zum Namen passt. In
Abschnitt 17 lädt dein eigenes src/ genau so, ohne eine einzige require-Zeile, und dort ist es
dann dein Code statt fremder.
Zum Mitnehmen
Autoloading ist keine Erfindung für Pakete. Es funktioniert für deinen eigenen Code genauso, und sobald es eingerichtet ist, hörst du auf, require-Zeilen zu schreiben.
Jetzt du
Basis Konto, kostenlosZu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.
Was in dieser Lektion steckt
-
Artikel mit 3 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe, auf dem Server geprüft
Öffnet sich mit dem Basis Konto.