PHP - Fehlerbehandlung im Autoloader
bearbeitet von
> Composer habe ich mir bisher nicht angeschaut, es klingt aber nach einer Wissenschaft für sich. Mal gucken. Im Moment bin ich mit meinem handgemachten Simpel-Autoloader, der fix einen Namespaceraum auf eine Verzeichnisstruktur mappt , noch zufrieden. Bis auf die genannten Details.
[Composer](https://getcomposer.org/) kann auf den ersten Blick etwas komplex wirken, da bin ich durchaus bei dir. Es ist allerdings mittlerweile auch das Standardtool für Autoloading/Package-Management in PHP. Wie es bei derlei Tools häufig der Fall ist, nutzt man auch bei Composer dann im Normalfall die meiste Zeit nur drei oder vier Funktionen.
Das Setup unter Windows [ist ein simpler Installer](https://getcomposer.org/doc/00-intro.md#installation-windows).[1] Wenn du das ausführst, solltest du danach in einem Terminal[2] `composer --version` tippen können. Wenn das was Sinnvolles ausgibt, ist das Tool funktionsfähig.
Ich glaube, es ist nicht sinnvoll, wenn ich jetzt die Doku nacherzähle, aber ein kurzes „Hello World“-Beispiel, das die maßgebliche Funktionalität demonstriert, möchte ich dennoch liefern. Ich erwarte natürlich nicht, dass du das sofort ausprobierst oder dein Projekt umbaust. Ich schreibe den Text auch, um für zukünftige Nutzung so was zu haben. Letztlich kann ich jedem PHP-Entwickler die Nutzung von Composer nur sehr ans Herz legen. Nebenbei möchte ich auch immer empfehlen, eine etwaige Scheu vor der Nutzung der Kommandozeile (Terminal-/Konsolen-Anwendungen) abzulegen. Eine Nutzung vereinfacht Workflows oft erheblich und macht viele Dinge überhaupt erst möglich.
Vorsichtshalber der Hinweis: Diese Kurzeinführung stellt eigentlich keine Operationen vor, mit denen versehentlich die falschen Dateien gelöscht oder überschrieben werden könnten, aber vor allem bei wichtigen Sachen schadet ein vorheriges Backup natürlich nie.
---
**Schnelleinstieg**
Wenn du irgendwo ein Verzeichnis (`myproject` oder so) für ein Demoprojekt anlegst, kannst du dort eine `composer.json`-Datei mit dem folgenden Inhalt hinzufügen:
~~~ json
{
"require": {
"league/commonmark": "^0.15"
}
}
~~~
Das würde aussagen, dass per Composer das [CommonMark-Package](http://commonmark.thephpleague.com/) (ein Markdown-Parser) eingebunden und verwaltet werden soll.[3]
Wenn du nun im Terminal in das `myproject`-Verzeichnis navigierst und dort `composer install` tippst, lädt Composer automatisch das Package herunter[4] und installiert es ins `vendor`-Unterverzeichnis.
~~~
$ composer install
Loading composer repositories with package information
Updating dependencies (including require-dev)
- Installing league/commonmark (0.15.0)
Loading from cache
league/commonmark suggests installing league/commonmark-extras (Library of useful extensions including smart punctuation)
Writing lock file
Generating autoload files
~~~
Im `vendor`-Verzeichnis wird zudem ein Autoloader generiert, den du nun nur noch in deinem eigentlichen Code einzubinden brauchst, um die Inhalte (Klassen, Interfaces, …) aller mit Composer verwalteten Packages zu nutzen. Dieser Autoloader wird per `require __DIR__ . '/vendor/autoload.php';` eingebunden.
Wenn du eine Datei `test.php` im Projektverzeichnis (`myproject`) erstellst und dort diesen Code einfügst…
~~~ php
<?php
require __DIR__ . '/vendor/autoload.php';
$converter = new League\CommonMark\CommonMarkConverter();
echo $converter->convertToHtml('# Hello World!');
~~~
…, hast du zum Beispiel ein fertiges Script, das Markdown-Quellcode in HTML umwandeln kann. Diese `require`-Zeile und die `composer.json`-Datei sind nahezu immer die einzige Anbindung, die Composer benötigt.
Wichtig ist an der Stelle: Composer wird in aller Regel nur lokal ausgeführt. Wenn du die `test.php` nun beispielweise auf einem Webspace ausführen möchtest (sozusagen ein Deployment des Projekts), musst du lediglich die Datei und das `vendor`-Verzeichnis hochladen. Composer selbst muss (und sollte) auf dem Zielsystem nicht installiert sein. Die fertige Software wäre also von den Abhängigkeiten und vom Aufbau her genauso, wie wenn du den Quellcode das [CommonMark-PHP-Packages](https://github.com/thephpleague/commonmark/releases) von Hand in ein Verzeichnis `vendor` kopiert und dort zudem einen Autoloader dafür erstellt hättest.
---
**Eigenen Code ebenfalls über den Composer-Autoloader einbinden**
Du schreibst davon, dass du deinen eigenen simplen Autoloader hast. Das ist natürlich völlig in Ordnung. Autoloading ist grundsätzlich ein gelöstes Problem und nichts, das komplizierter gemacht werden muss, als es ist.
Mit einem Code wie…
~~~ php
spl_autoload_register(function ($className) {
$path = __DIR__ . '/src/' . str_replace('\\', '/', $className) . '.php';
if (file_exists($path)) {
require $path;
}
});
~~~
…ist die Sache oft erledigt. Dieser Loader wäre auch perfekt kompatibel mit Composer, könnte also problemlos hinter der `require`-Zeile von Composer stehen.
Allerdings kann man, wenn man schon Composer nutzt, auch diesen Loader mit in Composer integrieren. Dazu wäre ein entsprechender `autoload`-Zusatz in der `composer.json` nötig, der in diesem Fall alle PHP-Klassen (und dergleichen) aus dem `src`-Unterverzeichnis per Autoloading verfügbar machen würde:
~~~ json
{
"require": {
"league/commonmark": "^0.15"
},
"autoload": {
"classmap": "src/"
}
}
~~~
Ich habe hier der Einfachheit halber die Classmap-Variante gewählt, da die prinzipiell immer funktioniert und dort Namespaces keine Rolle spielen. Eine Organisation im Dateisystem nach PSR-0 und PSR-4 [wird aber ebenfalls unterstützt](https://getcomposer.org/doc/04-schema.md#autoload). In deinem Fall wäre alternativ wohl PSR-4 angebracht. Dazu müsstest du bei Interesse kurz in die Doku schauen, weil bei dem Format der oberste Namespace mit angegeben werden muss, den ich natürlich nicht kenne.
Nach Änderungen in `composer.json` muss im Projektverzeichnis einmal `composer update` ausgeführt werden, um beispielsweise den Autoloader in `vendor` entsprechend zu aktualisieren. Ist dies geschehen, kann in diesem Fall der eigene Autoloader aus dem Code entfernt werden. Der Code in `src` kann nun auch mit `vendor/autoload.php` geladen werden.
---
**Warum Composer?**
Der wesentliche Einsatzzweck in eigenen Projekten liegt natürlich im Einbinden und Verwalten von Third-Party-Packages. Schreibt man selbst an Code, der von anderen Entwicklern eingebunden werden können soll, gehört es heutzutage zudem fast schon zum „guten Ton“, diese Library auch als per Composer nutzbares Package anzulegen.
Die eben vorgestellte Autoloading-Funktionalität für eigenen Code ist aber immer ein netter Bonus. Siehe dazu auch meine vorherige Antwort.
Eine `composer.json`-Datei eines privaten Projekts (Fußball-Tippspiel) von mir sieht beispielsweise so aus:
~~~ php
{
"config": {
"optimize-autoloader": true
},
"require": {
"roave/security-advisories": "dev-master",
"beberlei/assert": "~2.4",
"kaloa/renderer": "0.1.0",
"kaloa/view": "0.1.0",
"silex/silex": "~1.3"
},
"autoload": {
"psr-4": {
"Fuppes\\": "library/Fuppes"
}
}
}
~~~
Mein eigener Code liegt in `library/Fuppes` und nutzt PSR-4 (na ja, mehr oder weniger). Dazu nutze ich vor allem als Framework [Silex](http://silex.sensiolabs.org/) und einige andere Packages, die nicht so wichtig sind.[5] Das `vendor`-Verzeichnis, in das Composer die Packages installiert, enthält derzeit etwas über 1000 Dateien. Das braucht mich als Entwickler aber nicht groß zu interessieren, da Composer das alles für mich verwaltet. Ich schreibe in meiner Anwendung nur am Anfang einmal `require __DIR__ . '/vendor/autoload.php';` und kann dann alle Klassen nutzen.
---
1: Unter Linux habe ich es nicht so gemacht, wie es [in der Doku](https://getcomposer.org/doc/00-intro.md#installation-linux-unix-osx) vorgeschlagen wird. Ich habe einfach die `composer.phar` runtergeladen und sie als `composer` nach `~/bin` verschoben und dann vermutlich noch `chmod +x ~/bin/composer` gemacht. Ich glaube, der Grund dafür war, dass ich die Doku zu dem Zeitpunkt einfach nicht gesehen hatte und dass ich es mit [PHPUnit](https://phpunit.de/) aus diversen Gründen (IDE-Integration) auch immer so ähnlich mache. Es gibt jedenfalls verschiedene Möglichkeiten, die alle irgendwie zum Ziel führen.
2: Ein einfacher Weg, unter Windows eins zu öffnen, geht glaube ich so: Startmenü öffnen (Windows-Taste), `cmd` tippen, Enter drücken.
3: Zur Versionsangabe mit dem `^`-Zeichen siehe [hier](https://github.com/thephpleague/commonmark#versioning).
4: Über ein zentrales Repository namens [Packagist](https://packagist.org/).
5: Das Package [beberlei/assert](https://github.com/beberlei/assert) ist eine Empfehlung für einigermaßen schmerzfreie typsichere Programmierung vor PHP 7.