Modernes PHP-Dependency Management mit Composer: Der Website-Komponist
Das Konzept in aller Kürze: Mit Composer lässt sich eigener oder der Code von Drittanbietern projektspezifisch installieren und verwalten. Das System berücksichtigt dabei Abhängigkeiten automatisch. Verwendet eine API-Library also etwa Monolog als Logger, wird dieser bei der Installation ebenfalls geladen.
Außerdem wird fremder und eigener Code sauber getrennt und Projekte lassen sich einfach auf andere Entwicklungsumgebungen oder Server portieren. Es handelt sich also um ein Konzept für die PHP-Welt, das in anderen Sprachen längst bekannt ist und in denen vergleichbare Tools weit verbreitet sind. Und im Gegensatz zu PEAR erfährt es eine breite Unterstützung.
Abhängigkeiten
Für die zentrale Verwaltung von Abhängigkeiten nutzt Composer eine einfache JSON-Datei („composer.json“) im Root des Projekts. Werden etwa Monolog und das Facebook-SDK verwendet, landen beide Pakete mit Versionsnummern und weiteren Metadaten in dieser Datei. Die Versionsnummer kann auch mit Wildcards oder Range-Angaben spezifiziert werden. Anschließend lassen sich die Pakete mit einfachen CLI-Kommandos installieren oder aktualisieren. Entwicklungsumgebungen wie PhpStorm integrieren Composer bereits, sodass auch eine Verwaltung per Maus möglich ist.
Als öffentliches Standard-Verzeichnis steht mit Packagist [1] ein umfangreiches Repository zur Verfügung. Hier finden sich sowohl ganze Frameworks als auch kleine Pakete, etwa der eingangs erwähnte Monolog-Logger. Alternativ können eigene Repositories erstellt und selbst gehostet werden; eine interessante Option für Agenturen. Sogar eine direkte Github-Installation ist möglich; dies erfordert jedoch einige zusätzliche Angaben in composer.json.
Paketverwaltung mit Composer: Dokumentation erleichtert Überblick
Composer erstellt bei der Installation oder bei Updates eine weitere JSON-Datei („composer.lock“), in der Pakete mitsamt Versionsnummer und Abhängigkeiten dokumentiert sind. Der Inhalt dieser Datei bestimmt fortan, was bei weiteren install-Kommandos heruntergeladen wird. So ist sichergestellt, dass alle Mitarbeiter eines Projekts die exakt gleichen Paket-Versionen erhalten – sogar unabhängig vom Inhalt der composer.json. Damit das funktioniert, müssen composer.json und composer.lock entsprechend in Git oder SVN versioniert werden, im Gegensatz zu den heruntergeladenen Paketen selbst, die von der Versionierung ausgenommen sind.
Installierte Pakete liegen standardmäßig in einem so genannten „vendor“-Verzeichnis und können direkt im Projekt verwendet werden. Composer bringt dazu einen Autoload-Mechanismus mit, sodass man sich lästiges Inkludieren der Dateien und Verzeichnisse sparen kann – dazu später mehr.
Composer installieren: denkbar einfach
Die Installation von Composer ist denkbar einfach: Für Windows stehen EXE-Installer zum Download bereit, unter Linux, Mac OS und Unix erledigt eine phar-Datei die Arbeit. Außerdem bietet es sich an, die phar-Datei in den PATH zu verschieben, anstatt sie lokal im Projekt
zu speichern. Auf diese Weise ist Composer systemweit verfügbar. Noch einfacher ist die Installation übrigens mit einem Paketmanager, etwa mit Homebrew unter Mac OS.
$ curl -sS https://getcomposer.org/installer | php
Listing 1
$ brew install composer
Listing 2
Erste Pakete
Um mit Composer Abhängigkeiten im Projekt verwalten zu können, muss als Ausgangspunkt die composer.json erstellt werden [2]. Dafür steht mit „composer init“ ein Assistent auf der Kommandozeile zur Verfügung. Nach kurzem Frage-Anwort-Spiel ist die Datei mit grundlegenden Meta-Daten gefüllt. Das init-Kommando fügt das vendor-Verzeichnis außerdem der .gitignore hinzu.
{ "name": "dahal/t3n-composer", "description": "Ein Beispielprojekt", "minimum-stability": "dev", "authors": [{ "name": "Daniel Haller", "email": "d.haller@s-v.de" }], "require": { } }
Listing 3
Als nächstes werden im require-Block die eigentlichen Pakete und Abhängigkeiten definiert. Also Paket und Version via Packagist suchen (Konvention: „namespace/paketname“) und die require-Angaben in composer.json übernehmen.
"require": { "facebook/php-sdk": "v3.2.2" "monolog/monolog": "1.6.*@dev", "twitter/bootstrap": "v2.3.2“ }
Listing 4
Die Versionsangaben müssen nicht exakt sein. Twitters Bootstrap etwa wird auch als „dev-master“-Version angeboten. Darüber hinaus können Wildcards („v3.*“) und Ranges („>=2.*, <3.0“) angegeben werden [3].
Die anschließende Installation wird mit dem Kommando „composer install“ gestartet. Zum einen werden damit jene Pakete ins vendor-Verzeichnis heruntergeladen, die in composer.json definiert sind. Zum anderen erstellt Composer die lock-Datei und füllt sie mit detaillierten Informationen zu den installierten Paketen. Das install-Kommando löst außerdem weitere Abhängigkeiten auf: So benötigt Monolog noch das prs/log-Paket, das ebenfalls von Packagist geladen wird. Nach der Installation sieht das vendor-Verzeichnis daher folgendermaßen aus:
autoload.php composer/ --- ClassLoader.php --- autoload_classmap.php --- autoload_namespaces.php --- autoload_real.php --- installed.json facebook/ --- php-sdk monolog/ --- monolog psr/ --- log twitter/ --- bootstrap
Listing 5
Updates und Autoloading
Pakete aktualisiert Composer nicht automatisch, allerdings funktionieren Updates nach demselben Prinzip: Versionsnummer des Pakets in der composer.json ändern und mittels „composer update“ wird der Vorgang gestartet.Es lassen sich aber auch gezielt einzelne Pakete über Parameter spezifieren und entsprechend aktualisieren.
$ composer update monolog/monolog
Listing 6
Um die Pakete im Code einzubinden, stellt Composer einen Autoloader zur Verfügung. Durch das Einbinden der vom install-Kommando erzeugten Datei „vendor/autoload.php“ werden die Pakete im Vendor-Verzeichnis geladen. Im eigenen Projekt reicht dazu eine einzelne Zeile.
require 'vendor/autoload.php';
Listing 7
Pakete definieren Details des Autoloaders über einen „autoload“-Block in composer.json. Composer unterstützt dafür neben dem „PSR-0“-Standard eine Reihe bekannter Autoload-Mechanismen wie „classmap“ oder „files“. Die Parameter geben an, dass die Klassen des Monolog-Namespaces im Verzeichnis „src/“ liegen.
"autoload": { "psr-0": {"Monolog": "src/"} }
Listing 8
Eigene Pakete
Im Grunde ist jedes mit Composer neu aufgesetzte Projekt die Basis für eine neues Paket. Bei Verwendung des „init“-Kommandos wurden bereits alle notwendigen Angaben abgefragt, um das Paket auf Packagist oder Github zu veröffentlichen. Um ein Paket installieren zu können, ist lediglich die Angabe des „name“-Attributs in der composer.json zwingend erforderlich. Die externen Abhängigkeiten beziehungsweise Pakete von Drittanbietern, die das eigene Projekt benötigt, stehen bereits als „require“-Angaben zur Verfügung.
Soll das Projekt versioniert werden, kann manuell eine explizite Versionsnummer in der composer.json angegeben werden. Die Empfehlung von Composer hingegen ist, Branches und Tags des eigenen Versionskontrollsystems (VCS) zu nutzen. Tags werden dabei als Versionsnummern für das Composer-Paket interpretiert, wenn sie nach dem Schema X.Y.Z oder vX.Y.Z benannt sind. Branches werden, sofern sie eine Versionsnummer enthalten, mit dem Suffix „-dev“ zur Verfügung gestellt; alternativ ergänzt Composer das Präfix „dev-“. Entsprechend wird aus dem „master“-Branch in Git die Composer-Paketversion „dev-master“ [4].
PSR-Coding-Standards |
Die Standards „PSR-0“, „PSR-1“ und „PSR-2“ der Framework Interop Group definieren Coding-Standards, die von allen großen Frameworks genutzt werden. Der Standard „PSR-0“ ist dabei der kleinste gemeinsame Nenner. Er definiert, wie Namespaces und Klassen-Strukturen aufgebaut sein müssen, damit das Autoloading projektübergreifend funktioniert. Die PSR-Standards bilden so etwa die Basis dafür, dass eine Komponente des Zend-Frameworks auch in Symfony genutzt werden kann. |
Mit der nunmehr vollständigen composer.json kann
das Paket in ein VCS wie Github hochgeladen und anderen Entwicklern zur Verfügung gestellt werden. Um das Paket anschließend zu installieren, muss lediglich die Installationsquelle entsprechend geändert werden. Damit durchforstet Composer auf der Suche nach den im require-Block genannten Paketen nicht mehr ausschließlich Packagist, sondern eben auch die referenzierten Quellen. Entsprechend sähe die composer.json eines Projekts, dass das zuvor erstellte Paket nutzt, folgendermaßen aus:
{ "name": "dahal/t3n-newproject", "description": "Ein neues Projekt", "repositories": [ { "type": "vcs", "url": "https://github.com/dahal/t3n-composer" } ], "require": { "dahal/t3n-composer": "dev-master" } }
Listing 9
Die zusätzlichen Angaben im repositories-Block kann man anderen Entwicklern indes ersparen, indem man das Paket direkt auf Packagist veröffentlicht. Dazu ist lediglich eine Registrierung auf der Plattform notwendig, die anschließend mit dem VCS – wo der eigentliche Code nach wie vor liegt – verknüpft werden. Packagist durchsucht die Repositories und erzeugt daraus passende Composer-Pakete, wodurch die Plattform gewissermaßen als Proxy zwischen VCS und Benutzer fungiert.
Überprüfen von Paketen auf Sicherheitslücken |
SensioLabs, das Unternehmen hinter dem Symfony-Framework, bietet mit dem „Security Advisories Checker“ ein Online-Tool an, das Composer-Pakete auf bekannte Sicherheitslücken überprüft. Dazu muss lediglich die composer.lock-Datei aus dem eigenen Projekt hochgeladen werden. Die Seite listet anschließend projektbezogene Sicherheitshinweise. So ist ein gezieltes Update möglich. |
Fazit
Mit Composer lässt sich zeitgemäßes Dependency-Management in PHP-Projekten sauber integrieren. Die Entwicklung von einzelnen, gekapselten Modulen, die in verschiedenen Projekten genutzt werden können, steht im Vordergrund; eigener und fremder Code wird sauber voneinander getrennt. Composer schafft damit klare Strukturen und erleichtert die Verwaltung externer Bibliotheken stark.
Das öffentliche Haupt-Repository Packagist ist selbst ein Open-Source-Projekt auf Basis von Symfony2. Somit lässt es sich selbst hosten und eignet sich hervorragend für eigene (interne) Verzeichnisse. Alternativ bietet sich „Satis“ an, ein schlanker, dateibasierter und statischer Repository-Generator, der ideal für die Verwaltung weniger Pakete ist [5].