Dependency Management mit Composer

PHP befindet sich aktuell auf dem Weg, wieder eine angesehene Plattform für professionelle Webanwendungen zu werden. Dieses Ansehen muss sich erst wieder erkauft werden. Noch zu oft leidet die Sprache unter dem Bild, ihre Produkte bestünden nur aus schlecht strukturierten und unprofessionellen Scripts.
In vielen Fällen mag dies richtig sein und man muss zugeben, dass die Verwendung von PHP oft dazu verleitet, einen „Quick & Dirty“-Ansatz der sauberen Lösung vorzuziehen. Als professionelle Software-Entwickler müssen wir aber stets versuchen, mit unserer Arbeit hoch-qualitative und nachhaltige Software zu produzieren.

Ein Anspruch, den sich sicher jeder Entwickler stellen muss, ist, die Produkte seiner Arbeit wiederverwendbar zu halten. In diesem Kontext fallen oft die Begriffe Kapselung und Modularisierung. Um Module wiederverwendbar zu machen, müssen diese eine klare Schnittstelle besitzen und möglichst nur eine Aufgabe – und diese Aufgabe dafür richtig – erledigen. Beim Design modularer Applikationen merkt man schnell, dass bestimmte Module bereits an anderer Stelle entwickelt wurden und eventuell wiederverwendet werden können.
Dieser Artikel beschreibt eine Software, die das Wiederverwenden von PHP-Modulen automatisiert.


Composer

Composer ist eine Software, welche die Wiederverwendung von Quellcode vereinfacht und ein Modell für die Organisation von wiederverwendbaren Software-Komponenten vorschlägt. Ein Vorbild von Composer ist NPM, der Node Package Manager: Module heißen Pakete und werden durch eine JSON-Datei mit Metadaten versehen, in der beschrieben ist, welche Abhängigkeiten sie besitzen.

Bei Composer heisst diese JSON-Datei composer.json und beinhaltet sowohl die Metadaten des Moduls (Name, Version, Autoren, etc.), als auch die Liste seiner Abhängigkeiten.
Wie NPM regelt Composer nicht nur die Modul-Organisation, sondern auch die Beschaffung von benötigten Paketen. In einer Paket-Beschreibung ist es beispielsweise nicht nur möglich, eine Abhängigkeit auf die Software XYZLogger zu definieren, sondern Composer auch mitzuteilen, dass dieses Modul von Github aus dem Repository https://github.com/XYZ/Logger.git geladen werden soll.

Composer-Pakete

Ein Paket (engl. Package) ist im Composer-Jargon das, was im Sinne der Quellcode-Wiederverwendung ein Modul ist: Eine relativ große Einheit von Software, die wiederverwendbar gestaltet ist bzw. auch oft explizit für die Wiederverwendbarkeit in anderen Applikationen als Paket definiert wurde.

Zu Beginn eines Projekts, dessen Abhängigkeiten mit Composer verwaltet werden sollen, erstellt man ein Verzeichnis (, initialisiert dort optional ein Git-Repository) und startet Composer mit dem init-Befehl. Composer startet nun mit einer interaktiven Benutzeroberfläche, die einen nach dem Namen des Paketes, einer Beschreibung, dem Author und Abhängigkeiten fragt. Danach wird die Datei composer.json anhand der angegebenen Informationen generiert:

Wir beschreiben in diesem Artikel nicht die Installation von Composer. Eine Anleitung dazu findet man auf der Composer-Webseite.

In den Beispielen wird Composer immer durch $ composer aufgerufen (statt $ php composer.phar). Dies dient nur der Lesbarkeit und wurde durch einen simplen Symlink bei der Composer-Installation erreicht.

Im weiteren Verlauf des Projektes wird diese Datei hauptsächlich bzgl. der Abhängigkeiten des Moduls/Projekts erweitert. Da wir wissen, dass unser Beispielprojekt exzessiven Gebrauch von Logging machen wird, beschließen wir gleich zu Beginn, das Paket Monolog zu verwenden. Dazu reicht es aus, eine Version der Software als Abhängigkeit zu definieren und daraufhin Composer zu starten:

Composer installiert die spezifizierte Abhängigkeit dabei in ein konfigurierbares Verzeichnis (standard: vendor). Dort liegen später alle Abhängigkeiten sortiert nach „Anbieter“ (engl. vendor):

Neben dem gewünschten Modul Monolog hat Composer auch mehrere Dateien angelegt, die zum Laden der Klassen der abhängigen Module verwendet werden können. Dazu später mehr.

Definition von Abhängigkeiten

Bei der Definition von Abhängigkeiten muss für jedes Paket eine Angabe zur benötigten Version hinterlegt werden. Dabei kann man neben konkreten Versionsnummern wie 1.3.2 auch Versionsnummern mit Platzhaltern (1.3.*) oder Bereiche von Versionen wie z.B. >=1.3,<1.5 angeben. Innerhalb der erlaubten Versionen versucht dann Composer eine Version auszusuchen, die möglichst aktuell ist und keine Konflikte mit anderen Paketen erzeugt.

Neben der Version ist vor Allem noch die Installationsquelle eines Paket wichtig. Das Haupt-Nachschlagwerk für Composer ist die Seite Packagist.org, welche auch die Standard-Quelle für Composer-Pakete ist. Für Abhängigkeiten, die dort existieren, muss die Herkunft also nicht explizit angegeben werden. Dies ist z.B. auch der Fall bei Monolog.
Für Abhängigkeiten, die nicht auf Packagist veröffentlicht sind, muss man in der Datei composer.json ein Repository angeben, aus welchem das Paket geladen werden kann. Häufig möchte man in einem PHP-Projekt beispielsweise JavaScript-Bibliotheken wie JQuery verwenden, diese aber nicht im eigenen VCS-Repository ablegen. Eine Weg, dieses Problem zu lösen, ist es, das Github-Repository von JQuery zu referenzieren:

Wie oben zu sehen ist, hat ein Repository einen bestimmten Typ (type). Der Typ package wird verwendet, wenn das entsprechende Modul keine eigene Paket-Definition beinhaltet. Diese wird dann innerhalb der Repository-Definition nachgereicht.
Für Composer-Pakete, die über eine composer.json-Datei verfügen, jedoch nicht auf Packagist veröffentlicht sind, gibt es noch den Repository-Typen VCS, mit dem man solche Pakete aus einem Git-, Subversion- oder Mercurial-Repository verwenden kann. VCS-Repositories kommen oft zum Einsatz um Abzweigungen (engl. fork) von öffentlichen Modulen auf GitHub zu verwenden. Natürlich lassen sich aber auch lokale Repositories einbinden.

Referenz: Composer-Dokumentation zu Repositories

Classloading von Composer-Modulen

Um eine PHP-Bibliothek zu verwenden, muss man bekanntlich auch ihre Klassen im eigenen Projekt verfügbar machen. Die meisten aktuellen PHP-Applikationen verwenden dafür einen sog. Autoloader, der automatisch anhand gewisser Regeln herausfinden kann, in welcher Datei eine Klasse definiert ist. Ein Regelsatz, der die Zuordnung von Klassen zu Dateien beschreibt, ist mit PSR-0 gegeben.

Hält man innerhalb seines Moduls diese Regeln ein, kann Composer einen Autoloader generieren, der die Klassen dieses Moduls lädt. Dazu müssen in der Paketbeschreibung lediglich die
angeben, welcher PHP-Namespace aus welchem Ordner innerhalb des Pakets geladen werden soll:

Für Projekte, die das beschriebene Paket verwenden, wird die Datei vendor/autoload.php generiert, die – wenn eingebunden – dafür sorgt, dass die Klassen des Namespaces Seiffert im src-Verzeichnis des Pakets gesucht werden.

Referenz: Composer-Dokumentation zu Autoloading

Weitere Paket-Metadaten

Diese kurze Einführung hat natürlich nicht alle erlaubten Attribute eines Composer-Pakets beinhaltet. Die komplette Liste liest man am besten auf der offiziellen Website nach.

Ich persönlich hoffe, dass sich Composer zum Standard für PHP-Projekte entwickelt, da seine Bedienung durch Einfachheit überzeugt und er Entwicklern viele Mühen erspart, die sonst beim Zusammensuchen der notwendigen Libraries aufkommen. In der Entwicklung von Symfony2-Bundles hat Composer diesen Durchbruch bereits geschafft, die allermeisten Bundles befinden sich entweder bereits auf Packagist oder mit einer composer.json auf GitHub.

Da Composer selbst auch in PHP entwickelt wurde, ist die Integration des Tools in die eigenen Anwendungen sehr reizvoll. Aktuell arbeiten wir an einer Integration in Symfony2-Projekte, die wir in naher Zukunft hier präsentieren möchten.

Dieser Eintrag wurde veröffentlicht in PHP und verschlagwortet mit , , , , , von Paul Seiffert. Permanenter Link zum Eintrag.

Über Paul Seiffert

Paul ist seit Oktober 2010 ein Vollmatrose der Mayflower und arbeitet in einem Team von 6 Entwicklern als Lead-Architekt und stellvertretender Teamleiter. Zuvor studierte er an der TU München Informatik mit den Schwerpunkten Software-Engineering und Algorithmik. Sein Tech-Set umfasst sowohl PHP- als auch Javascript-Technologien wie Zend Framework 1, Symfony2, Node.js und Backbone.js. Paul ist immer an neuen Technologien und Vorgehensweisen interessiert und schreibt gerne über Neuerlerntes. Einige seiner Artikel sind hier auf http://blog.mayflower.de zu finden. Twitter: @SeiffertP Github: seiffert

Für neue Blogupdates anmelden:


Ein Gedanke zu “Dependency Management mit Composer

  1. Sweetass bro!

    Das ist definitiv der weg den momentan viele gehen. Bei GNOME haben wir mit jhbuild gearbeitet und jetzt schau ich mir gerate ROS (www.ros.org) an, die haben auch ein dependency system wo man einfach abhängigkeiten modellieren kann und automatisch herunterladen kann.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.