Ich habe heute ein kleines Plugin für WordPress angefangen und versucht, mich für die Struktur und grundsätzliches ein bisschen am Speaking-Plugin von Alain Schlesser zu orientieren. So bin ich auf das Thema Autoloading gekommen. Alain hat für sein Plugin einen eigenen Autoloader geschrieben, in seinem Talk beim WordCamp Nijmegen aber auch die Möglichkeit via Composer angesprochen. Hier zeige ich, wie sich ein Autoloader mit Composer umsetzen lässt.
Voraussetzung für das kleine Tutorial ist natürlich Composer. Nun führen wir in unserem Projekt composer init
aus und geben ein paar Informationen ein – als Ergebnis erhalten wir eine composer.json
.
Jetzt muss in der composer.json
noch eine kleine Erweiterung vorgenommen werden, damit im nachfolgenden Schritt der Autoloader erstellt werden kann. In meinem Plugin sieht der Teil so aus (hilfreich für die ganze Autoloader-Sache mit Composer war ein Artikel von phpenthusiast.com):
"autoload": {
"psr-4": {
"FlorianBrinkmann\\CustomizeThemesInstaller\\": "src/"
}
}
Code-Sprache: PHP (php)
Wir mappen den Namespace FlorianBrinkmann\CustomizeThemesInstaller
auf das src
-Verzeichnis, sodass zum Beispiel die Klasse FlorianBrinkmann\CustomizeThemesInstaller\Plugin
in der Datei src/Plugin.php
gesucht wird (bei PSR-4 muss mit Namespaces gearbeitet werden).
Nun führen wir composer dump-autoload
aus, um den Autoloader zu erstellen, und binden ihn über require_once __DIR__ . '/vendor/autoload.php';
in unsere Bootstrap-Datei ein.
Für den Produktiv-Einsatz wird empfohlen, den Autoloader zu optimieren, sodass er nicht mehr das Dateisystem prüfen muss, bevor ein Klassenname aufgelöst wird. Verschiedene Wege dazu werden in der Dokumentation von Composer aufgeführt.
Hallo Florian,
Danke für die Verlinkung!
Toll dass du dich sofort mit Composer auseinandersetzt, das ist meines Erachtens nach heutzutage Pflichtprogramm in der PHP Entwicklung.
Hier noch ein paar Verbesserungsvorschläge zu deinem Autoloader:
\\
in der Composer Konfiguration, alsoFlorianBrinkmannCustomizeThemesInstaller\\
, ansonsten werden nämlich auch Klassen da reinfallen, die diesen namespace als Teil-kette beinhalten.\
als Rootnamespace, und dann eventuell noch Module oder sonstige Einteilungen als weitere Unterteilugen. In deinem Fall also so etwas wie:FlorianBrinkmann\CustomizeThemesInstaller
.vendor
-Verzeichnis dann im Rootverzeichnis der Webseite landet. Also so in etwa wie hier: https://github.com/brightnucleus/jasper-client/blob/master/tests/bootstrap.php#L55-L59Hoffe das kann dir weiter helfen!
Hi Alain,
danke für deinen Kommentar!
Sehr gerne, danke für deine ganzen Inhalte rund um PHP und OOP 🙂
Zu den ersten beiden Punkten: Das war ursprünglich im Beitrag so (und ist es jetzt wieder), irgendwie scheint MultilingualPress Backslashes zu entfernen, die in dem jeweils verknüpften Beitrag stehen … Gleich mal einen Bug-Report erstellen.
Zu dem dritten Punkt: Das Root-Package muss dann die Autoloader-Funktion von Composer nutzen (also
vendor/autoload.php
einbinden), und da wäre das Autoloading für meine Klassen auch mit dabei? Aber was mache ich, wenn das Root-Package die Autoloading-Funktion von Composer nicht nutzt?Auf jeden Fall, das tut es!
Viele Grüße
Florian
> Aber was mache ich, wenn das Root-Package die Autoloading-Funktion von Composer nicht nutzt?
Das würde normalerweise nicht passieren. Das Root-Projekt nutzt ja Composer, um seine Abhängigkeiten zu verwalten. Diese sind aber nicht nutzbar (da für PHP nicht vorhanden) wenn der Autoloader nicht geladen wird.
Und sobald der Autoloader geladen wird, lädt der alle Namespaces von allen Abhängigkeiten, auch von deinem Plugin.
Okay, klingt logisch, danke!
Hallo Florian,
vielen Dank für deinen Blog-Beitrag. Ich verfolge das Thema seit dem Beitrag von Alain auch mit großem Interesse und konnte inzwischen auch einen einfachen Autoloader erstellen, scheitere aber am automatisch per Composer erzeugten Autoloader.
Der Autoloader wird zwar geladen, aber ein $plugin = new Admin\Plugin(); wirft penetrant eine Fehlermeldung aus, auch wenn die Klasse Plugin in src/Admin.php vorhanden ist. Wahrscheinlich würde mir ein kurzes Code-Schnipsel auf Github sehr weiterhelfen. Vielleicht geht da noch was? 🙂
Ich glaube, in der Zeile "… sodass zum Beispiel die Klasse FlorianBrinkmann\CustomizeThemesInstallerPlugin in der Datei src/Plugin.php gesucht wird" fehlt im Namespace noch ein Backslash hinter "Installer"?
In den WordPress Coding Standards wird empfohlen, Dateien mit Klassen nach dem Muster class-klassenname.php zu benennen. (https://make.wordpress.org/core/handbook/best-practices/coding-standards/php/#naming-conventions) -- lässt sich das mit dem automatisch generierten Autoloader überhaupt gescheit abbilden?
Hi Bego,
sorry für die späte Antwort, AntispamBee war etwas übereifrig …
Welche Fehlermeldung genau? Fehlt eventuell ein Backslash am Anfang, also:
$plugin = new \Admin\Plugin();
Ich hab es mal auf GitHub gepackt, vielleicht hilft es dir: https://github.com/florianbrinkmann/fbn-themes-customize-installer 🙂
Ja, richtig – danke für den Hinweis, hab ich angepasst! 🙂
Da bin ich etwas überfragt … Wenn ich mir hier (http://www.php-fig.org/psr/psr-4/) den Punkt 3.3 anschaue (»The terminating class name corresponds to a file name ending in .php. The file name MUST match the case of the terminating class name.«), würde ich sagen, dass es zumindest mit dem PSR-4-Ding nicht funktioniert.
Viele Grüße
Florian
Hi Bego,
Hast du den Code irgendwo sichtbar, damit man dir helfen kann den Fehler zu finden?
Wegen den Klassennamen in den WordPress Code Standards musst du wissen, dass diese eigentlich für die WordPress Core Entwicklung gedacht sind. Viele Plugins halten sich da zwar auch dran, aber das ist weder nötig noch sinnvoll, meines Erachtens nach.
Der PSR-4 Standard definiert in der Tat dass Klassen in "StudlyCaps" geschrieben werden, und das die Verzeichnis 1:1 mit den Namespaces and die Dateien 1:1 mit den Klassennamen übereinstimmen. Das nutzt auch mittlerweile so ziemlich die ganze PHP Welt.
Composer kann aber auch anders. Im Falle von WordPress kannst du z.B. eine "classmap" benutzen. Dann liest Composer alle Dateien ein, und verknüpft beliebige Dateinamen mit Klassen die beinhaltet sind. Dabei kannst du sogar mehrere Klassen in einer Datei haben, wie das bei WordPress auch leider üblich ist. Der Nachteil: das ist dann eine generierte Liste, und wenn du eine Klasse hinzufügst, dann musst du die "classmap" neu generieren.
Danke, Florian und Alain, für die Hilfestellungen. Das Beispiel auf Github bringt mich schon weiter. Ich war wohl von einigen falschen Annahmen ausgegangen (z.B. dass $plugin = new Admin/Plugin(); eine Klasse Plugin in einer Datei ./src/Admin.php öffnet und jede Klasse auch ihren eigenen (Sub-)Namespace haben sollte). Aber ich werde noch ein wenig ausprobieren und lerne gerne dazu. 🙂
Als Laie freue ich mich immer, wenn ich in Themes und Plugins von Dritten den PHP-Code gute lesen und schnell nachvollziehen kann. Dazu gehört, dass Code "sauber" (im Sinne von durchgängig, einheitlich) formatiert ist. Um den eigenen Ansprüchen zu genügen, finde ich den Einsatz von PHP Codesniffer sehr hilfreich -- regelmäßig wird Code korrigiert, den ich schludrig formatiert habe. Wenn ich aber sowieso PHP Codesniffer verwende, kann ich auch gleich die WordPress Coding Standards nutzen. Ich finde die Tipps (z.B. in WordPress-Extra der Hinweis, eine Variable nicht einfach auszugeben, ohne sie vorher zu escapen) hilfreich und sehe mich gar nicht eingeengt. YMMV.
Normalerweise unterscheidest du zwischen dem "Root-Namespace" (= Konflikte vermeiden) und den dazu relativen Unter-Namespaces (= interne Organisation). Eine weit verbreitete Konvention ist es, als Root-Namespace `\` zu benutzen, also so etwas wie `BegoMarioGarde\MyPlugin`. Die Unter-Namespaces benutzt du dann für Module, Funktionsbereiche, Objektarten, oder andere organisatorischen Einteilungen. Das oben erwähnte `Admin` könnte z.B. so ein Unter-Namespaces sein. Wenn du nun in diesem `Admin` eine Klasse `SettingsPage` hättest, dann sähe die gesamte Konfiguration so aus:
- Composer würdest du den Root-Namespace nennen und ihn einem Verzeichnis zuordnen (üblicherweise `src/`).
- Im `src/` Verzeichnis würdest du ein Unterverzeichnis `Admin` erstellen.
- Im `src/Admin/` Verzeichnis würdest du eine Datei `SettingsPage.php` erstellen.
- In der `SettingsPage.php` Datei nutzt du den Namespace `use BegoMarioGarde\MyPlugin\Admin;`.
- Wenn du nun in dieser Datei bei dem Namespace eine Klasse `class SettingsPage {}` erstellst, so hat diese den "fully qualified class name" (FQCN) `BegoMarioGarde\MyPlugin\Admin\SettingsPage`.
Wegen PHPCS stimme ich dir zu, meine Bemerkung war eher wegen der Datei-Einteilung. Standards sind eine wichtige Sache (und es gibt mehrere gültige davon, neben dem WPCS, je nach Geschmackssache). Die Sache mit den Klassen-Dateien hat halt technische Nachteile, die PSR-4 vermeidet.