Autoload in PHP

Wie man meinen Artikeln in den letzten Wochen entnehmen kann, befasse ich mich derzeit mit der Entwicklung von PHP. PHP habe ich schon früher programmiert, sei es Erweiterungen für das von Wikipedia bekannte Mediawiki oder diverse Webseiten, etwa die des Unternehmens, in dem ich arbeite oder meine eigene.

Zuletzt verwendete ich PHP hauptsächlich dazu, Daten in einer MySQL-Datenbank, auf meinem Web-Space nach außen hin zugänglich zu machen. Z.B. habe ich ein C#-Programm, mit dem ich Koch-Rezepte verwalten kann. Die Daten liegen in einer MySQL-Datenbank und ich rufe sie über einen Rest-Service ab.

 

Schöner Code mit PHP?

Diese Schnittstelle galt es nun noch einmal neu zu machen, da ich den Code so wie er war nicht mehr zum laufen bekommen hatte (das lag an einer Dritthersteller-Komponente, dessen Funktionsweise ich wohl vergessen hatte).

Diesmal wollte ich aber nicht wieder einfach PHP-Seiten erstellen und tonnenweise Funktionen da rein packen. Funktionsorientierte Programmierung gefällt mir mittlerweile gar nicht mehr. Das muss doch besser gehen!

Auf den richtigen Weg hat mich die Seite PHP: the right way gebracht. Hier wird erklärt, wie man eine PHP-Anwendung sauber und objektorientiert strukturiert. Hier hat mir insbesondere der Abschnitt Namespaces gefallen. Die lästig langen Listen von include oder require am Anfang von Quellcode-Dateien gehören damit der Vergangenheit an, wir arbeiten jetzt nur noch mit use auf Basis von Namespaces, was im Grunde einem import in Java oder using in .Net entspricht.

 

Was ist Autoload?

Autoload ist im Grunde ein Konzept, mit dessen Hilfe es möglich ist, PHP-Dateien aus einem Namespace automatisch zu laden, wenn diese zur Laufzeit noch unbekannt sind. Das entspricht in etwa dem Konzept der Dependency Injection. Wir müssen uns nicht mehr darum kümmern, woher eine Klasse kommt, sie ist einfach da. Der Unterschied ist nur, dass wir sie hier noch selbst instanzieren müssen.

 

Wie funktioniert Autoload?

Seit PHP5 gibt es die sogenannte Autoload-Funktion. Hierüber lassen sich Klassen laden, wenn PHP diese zum Zeitpunkt der Verwendung noch nicht kennt.

In PHP 5.1.2 ist nun eine Regsitrierung für Autoloader dazugekommen (spl_autoload_register). Dadurch ist man nicht mehr darauf beschränkt, einen Autoloader zu nutzen, sondern kann verschiedene Autoloader registrieren, die dann alle verwendet werden, wenn eine Klasse geladen werden soll.

Durch diese Erweiterung ist Autoload ein mächtiges Werkzeug, um Ordnung in das Chaos der PHP-Dateien zu bekommen. Dadurch lassen sich nun z.B. für alle verwendeten Bibliotheken eigene Autoloader registrieren (welche diese in aller Regel mitbringen) und man muss sich darum schon nicht mehr selbst kümmen.

Wir brauchen also nur noch einen Autoloader für unsere eigenen Klassen. Aber auch das hat mich einige Zeit gekostet, dahinter zu kommen, wie dies sauber funktioniert. Eine sehr große Hilfe war der Artikel von Jess Telford in seinem Blog. Auf diesem Artikel basiert auch das folgende Beispiel.

 

Beispiel

Nehmen wir als Beispiel das oben genannte Projekt, in dem Kochrezepte gespeichert werden. Wir haben eine Klasse EntityRepository (das sind Messeinheiten, etwa Liter oder Gramm, für die Angabe zu einer Zutat) in einer eigenen PHP-Datei. Diese benötigt das Model Entity und die Klasse PDO für den Zugriff auf die Datenbank (darauf kommt ich später in einem anderen Artikel nochmal drauf zurück).

Früher war alles Besser?

Nach dem alten Verfahren würde die Datei in etwa so aussehen:

Würden wir nun ein paar Klassen in einer anderen benötigen, müssten wir all diese in include-Anweisungen angeben. Die Liste kann mitunter sehr lang werden.

 

Der AutoLoader

Schreiben und registrieren wir nun also einen Autoloader.

 

Über die Methode registerDirectory(…) können wir dem Autoloader Verzeichnisse übergeben, in denen er zur Laufzeit nach neuen Klassen suchen soll. Zur Laufzeit selbst wird dann, wenn PHP eine Klasse noch nicht kennt, die Methode loadClass(…) aufgerufen, um die Klasse zu laden.

Die Klasse AutoLoader speichert alle registrierten Klassen in einem assoziativen Array ab. Dabei wird der vollqualifizierte Name der Klasse registriert.

 

Den Autoloader konfigurieren

Als nächstes müssen wir den AutoLoader noch mit den Verzeichnissen füttern, in denen unsere Klassen liegen. Das erfolgt ganz klassisch über einen PHP-include:

 

Ich habe in meinem Beispiel also vier Verzeichnisse, in denen der Autoloader nach Klassen suchen soll. Der Autoloader merkt sich dann alle PHP-Dateien aus allen angegebenen Verzeichnisse.

Kommt ein neues Verzeichnis hinzu, muss dies ebenfalls einmalig registriert werden.

Diese Konfigurationsdatei muss natürlich auch einmal per include eingebunden werden, etwa in der index.php.

 

Der AutoLoader in Aktion

Habe ich den AutoLoader einmalig konfiguriert, stehen mir alle Klassen unter dem vollqualifizierten Namen zur Verfügung. Um diese festzulegen, nutze ich Namespaces.

Als Beispiel die Entity-Klasse von oben:

Die Klasse Entity liegt im Verzeichnis model. Durch die Angabe des Namespace model ist die Klasse nun unter dem vollqualifizierten Namen model\Entity zu finden. Verwenden wir in PHP Namespaces, verwendet PHP bei der Suche nach der Klasse immer die vollqualifizierten Namen.

 

Unsere Klasse EntityRepository sieht also nun so aus:

Ich kann das include weglassen, ich muss nicht wissen, wo sich die Klasse befindet. Verschiebe ich die Klasse, findet der AutoLoader sie trotzdem und ich muss nicht alle include-Anweisungen korrigieren.

Hinweis: Ich verwende hier noch use für die Entity-Klasse. Der Grund ist dabei nicht, dass PHP die Klasse nicht auch so finden würde. Der Grund ist, dass mir IntelliJ dann sehr viel Unterstützung bietet, was Code-Vervollständigung und Prüfung angeht.

 

Verzeichnisstruktur

Ein Problem das ich bei der Beschäftigung mit dem Thema noch hatte ist der der Verzeichnisstruktur. Wir baue ich die Verzeichnisse sauber auf, so, dass nachher auch noch alles funktioniert?

Hier mal ein Beispiel:

  • \dispatcher
    Funktionen zum Routing innerhalb der Anwendung
  • \json
    Klassen, die den Umgang mit JSON ermöglichen oder vereinfachen
  • \model
    Die Models der Anwendung, also Recipe, Ingredient, …
  • \repository
    Die Repositories für den Zugriff auf die Daten
  • \test
    Unit- und E2E-Tests
  • \vendor
    Externe Bibliotheken, die mithilfe von Composer hinzugefügt werden. \vendor ist das Standardverzeichnis für Composer-Abhängigkeiten.
  • autoloadConfiguration.php
    Die oben beschriebene Konfiguration es AutoLoaders.
  • AutoLoader.php
    Die AutoLoader-Klasse selbst
  • composer.json
    Die Konfiguration von Composer
  • index.php
    Unsere Einstiegsseite. Meine sieht für obiges Projekt recht überschaubar aus:

 

Diese Struktur funktioniert für mich sehr gut. Vielleicht hilft es ja dem ein oder anderen da draußen.

 

Fazit

Mit der Autoload-Funktionalität bekommen wir PHP-Entwickler eine gute, flexible Methodik an die Hand, um unsere Klassen ordentlich zu strukturieren. Wir müssen uns an ein paar Regeln halten (die im PSR-4 beschrieben sind), diese sind uns aber bereits dann bekannt, wenn wir schonmal versucht haben, sauberen Code in einer objektorientierten Programmiersprache zu schreiben.

Alles in allen sollte das PHP-Leben mit dem obigen Beispiel und einer sauberen Struktur aufgeräumter werden.

 

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.