Composer verstehen - Teil 1 - Basics

von Marco Simbürger

Composer ist zu einem unerlässlichen Bestandteil bei der Web-Entwicklung geworden. Dies kann aber für diejenigen, die keine Erfahrung mit der Console haben, entmutigend sein oder für die, welche die Console und Composer verwenden, trotzdem noch verwirrend sein. In diesem Blog-Beitrag möchte ich versuchen, die Thematik "Composer" einfach zu erklären.

Composer: Was ist das?

Wikipedia beschreibt Composer wie folgt:

Composer is an application-level package manager for the PHP programming language that provides a standard format for managing dependencies of PHP software and required libraries.

Diese Beschreibung ist etwas kompliziert. Also lassen wir uns etwas weiter unterteilen, um zu verstehen, was das genau bedeutet.

Programmierer verwenden gerne den Begriff DRY - "Don't Repeat Yourself". Dies bedeutet, dass Code nach Möglichkeit wiederverwendet und nicht neu geschrieben werden sollte. Klassischerweise bezog sich dies auf Code innerhalb der Codebasis einer einzelnen Anwendung, aber mit Composer kann Code nun auch zwischen Anwendungen ausgetauscht werden. DRY meint auch "das Rad nicht neu erfinden". Wenn jemand anderes bereits Code (ein Modul, eine Bibliothek, ...) geschrieben hat, der das tut, was man selbst auch braucht, ist es besser diesen Code wieder zu verwenden als ihn selbst zu schreiben.

Ein Beispiel: Der aktuelle Standard für die Authentifizierung (Anmeldung) an entfernte Systeme ist beispielsweise das OAuth2 Protokoll. Dies ist ein sicheres Protokoll, das es Websites oder Anwendungen ermöglicht, sich bei anderen Websites wie Facebook, Google, Twitter, Instagram und unzähligen anderen zu authentifizieren. Das Schreiben einer OAuth 2 Integrationen ist komplex. Andere Entwickler haben jedoch bereits Code geschrieben, der die Integration von OAuth 2 übernimmt und haben diesen Code in Form eines Pakets im Internet veröffentlicht. Ein Paket ist im Grunde genommen ein Ansammlung von Code, der von anderen Websites wiederverwendet werden kann. Mit Composer können diese Pakete nun in ein Projekt eingebunden werden. So zum Beispiel eine Authentifizierung via Oauth2 - man muss diesen Code nicht nochmals selbst schreiben.

Composer ermöglicht es, folgendes zu tun:

  • Herunterladen und Einbinden eines Pakets in ein Projekt
  • Herunterladen und Einbinden aller Pakete, von denen das eine Paket abhängig ist
  • Überprüfung, ob die Pakete die Systemanforderungen erfüllt
  • Überprüfung, ob es keine Versionskonflikte zwischen den Paketen gibt
  • Aktualisieren der Pakete und deren Abhängigkeiten

Wie funktioniert Composer?

Composer selbst ist eine Software und muss installiert werden. Siehe dazu Composer installieren

Nachdem man Composer installiert hat, kann man einfach gesagt sagen: "Composer: Lade Paket A in mein Projekt". Composer durchsucht dann die Repositories nach passenden Paketen.

  • Ein Projekt ist die Codebasis, in der Regel für eine Website oder Anwendung, die von Composer verwaltet wird.
  • Ein Repository ist ein Server, der eine Sammlung von Paketen zum Herunterladen bereitstellt. Wenn Composer Paket A in einem Repository findet, lädt er dieses Paket, sowie alle Pakete herunter, von denen das Paket A abhängig ist.

Standardmässig ist das Haupt-Repository, das Composer anfragt, packagist.org. Diese Seite wurde speziell für Composer eingerichtet und enthält unzählige von öffentlichen Paketen, die von Entwicklern zur Verfügung gestellt wurden. Wenn ein Benutzer "Lade Paket A in mein Projekt" sagt, sucht Composer nach dem Paket A auf packagist.org und wenn Composer das Paket findet, lädt er diese herunter. Wenn das Paket A von Paket B abhängt, dann wird auch Paket B heruntergeladen, und so weiter.

Composer kontrolliert ebenfalls, ob das lokale System die Mindestanforderungen erfüllt, um sowohl Paket A, Paket B als auch alle anderen Abhängigkeiten zu handhaben. Composer prüft zudem, ob eines dieser Pakete Konflikte mit anderen Pakete aufweist. Wenn Konflikte gefunden werden, zeigt Composer einen Fehler an und installiert die Pakete erst, wenn alle Konflikte behoben wurden. Konflikte können zum Beispiel zwischen Abhängigkeiten bestehen, wenn Paket A zwingend auf eine bestimme Version von Paket B angewiesen ist.

Während packagist.org für Composer das Standard-Repository ist, können in Projekte auch benutzerdefinierte Repositories angegeben werden. Viele Entwickler verwenden beispielsweise Github oder Bitbucket, um ihren Code in der Cloud zu speichern. Man kann Composer also auch so einrichten, um nach Paketen in seinem privaten Github, Bitbucket oder anderen Repositories zu suchen und von dort herunterzuladen. Dadurch können sowohl öffentliche als auch der private Pakete eines Projekts mit Composer verwaltet werden.

Was passiert, wenn ich ein Paket installiere?

Composer verwaltet Projekte auf technischer Ebene mit zwei Dateien: compser.json und composer.lock. Zuerst schauen wir uns die Datei composer.json an. Diese Datei beschreibt quasi das Projekt. Alle Pakete, von denen das Projekt abhängt, werden in dieser Datei gespeichert. Die composer.json kann auch verwendet werden, um bestimmte Ordnerpositionen festzulegen, in die Pakete installiert werden sollen oder um Scripte einzurichten, die im Rahmen eines Composer-Prozesses (install, update, ...) ausgeführt werden sollen. Wenn man private Repositories verwendet, werden diese Repositories auch in dieser Datei deklariert. Die Datei ist eigentlich "der Entwurf" des gesamten Projekts.

Jedes Paket hat einen Namen. Der Name besteht aus zwei Teilen. Der erste Teil ist der Anbieter (vendor) des Pakets. Dieser kann eine beliebige Zeichenkette sein, ist aber oft der Firmenname, der Github-Benutzername usw. Der zweite Teil ist der Name des Pakets. Die beiden Teile sind durch einen Schrägstrich getrennt und enthalten nur Kleinbuchstaben. Pakete werden mit dem Befehl require von Composer installiert. Beispiel:

$ composer require terminal42/notification_center

Wenn die oben genannten Befehle ausgeführt werden, lädt Composer das Paket und dessen Abhängigkeiten herunter und fügt das Paket der composer.json Datei hinzu, um anzuzeigen, dass das Projekt dieses Paket verwendet. Das bedeutet, dass composer.json im Wesentlichen eine Metadatendatei ist, die die Codebasis des Projekts beschreibt.

Composer und Git, mehrere Umgebungen und mehrere Entwickler

Composer und Git arbeiten sehr gut zusammen. Um zu verstehen, wie das geht, werfen wir zunächst einen Blick auf die klassische Website-Verwaltung mit Git, ohne Composer.

Ein Beispiel: Entwickler A erstellt ein neues Projekt, das rein mit Git verwaltet wird (ohne Composer):

  1. Entwickler A lädt ein CMS herunter (Contao, Drupal, ...)
  2. Entwickler A erstellt ein neues Git-Repository für den heruntergeladenen Code und überträgt den Code in das Repository
  3. Entwickler A lädt den Code in ein zentrales Repository (z.B. Github oder Bitbucket)
  4. Entwickler A checkt den Code auf dem Test- und Produktionsserver usw. aus


Stellen wir uns nun vor, dass Entwickler B auf das Projekt kommt. Entwickler B verwendet Git, um den Code aus dem zentralen Repository herunterzuladen. An dieser Stelle existiert die Codebasis in Git an mehreren Stellen:

  • Auf dem Computer von Entwickler A
  • Auf dem Computer von Entwickler B
  • Im zentralen Git-Repository
  • Auf dem Testserver
  • Auf dem Produktionsserver
  • usw.

Im Moment besteht die Codebasis nur aus dem CMS Core. Der Code wird über Git verwaltet, was es ermöglichen würde, Änderungen im Code zu verfolgen. Jedoch ist es sehr unwahrscheinlich, dass entweder Entwickler A oder Entwickler B oder andere Entwickler tatsächlich jemals eine diese CMS Core Dateien bearbeiten werden - denn das sollte man niemals tun! Der Core sollte nur von Core-Entwicklern bearbeitet werden, die diesen entwickeln, nicht von Projekten, die ihn einfach verwenden. Das obige Setup führt also dazu, dass ein Haufen Code im Repository unnötig geteilt und verfolgt wird. Der Code ist ja bereits woanders geteilt (z.B. Contao auf Github oder Drupal auf git.drupal.org) und wird von dessen Core-Team verwaltet und weiterentwickelt. Es ist also überflüssig, den kompletten Code in sein eigenes Repo zu bringen.

Nun schauen wir, wie wir das Projekt mit Composer aufsetzen würden.

  1. Entwickler A erstellt ein neues Projekt mit Composer
  2. Entwickler A erstellt ein neues Git-Repository und fügt composer.json und composer.lock zum Git-Repository hinzu
  3. Entwickler A pushed die Dateien composer.json und composer.lock ins zentrale Repository
  4. Entwickler A richtet den Test- und Produktionsserver usw. ein und checkt den Code für diesen Servern aus
  5. Entwickler A führt die Composer-Installation auf den Servern aus. Daraus ergeben sich alle Anforderungen und Abhängigkeiten für das Projekt, wie sie in composer.json definiert sind

Wenn nun Entwickler B mit dem Projekt beauftragt wird, verwendet er Git, um die Codebasis auf den lokalen Computer herunterzuladen. Diese Codebasis enthält nun composer.json und composer.lock. Wenn Entwickler B jetzt die Composer-Installation ausführt, erhält er genau die gleiche Codebasis wie auf der Maschine von Entwickler A.

Nun existiert die Codebasis an mehreren Stellen, aber der einzige Code, der im Git-Repository verfolgt wird, sind die beiden Dateien, die zur Definition des vom Composer verwalteten Projekts verwendet werden (und im Normalfall noch weitere benutzerdefinierte, Projekt spezifischen Dateien).

Wenn ein Update für das Projekt durchgeführt wird, wird es durch Ausführen von composer update durchgeführt, das sowohl composer.json als auch composer.lock aktualisiert. Diese Dateien werden dann im Git-Repository aktualisiert, da es sich um die für das Projekt spezifischen Dateien handelt.

Der Unterschied zwischen der traditionellen Git-Methode und der obigen Methode mit Composer besteht darin, dass der CMS Core nun als externes Paket betrachtet wird und nicht unnötig Platz im Git-Repository des Projekts einnimmt.

Projekt-Versionen

Projekte haben so gut wie immer Versionen. Contao, Drupal usw. verwenden semantische Versionierung, was bedeutet, dass es die Versionen 4.1, 4.2, 4.3... und so weiter durchläuft. Nehmen wir an, die aktuelle Version ist 4.6.0. Wenn ein neuer Sicherheitsfix veröffentlicht wird, ist das 4.6.1. Mit der Zeit wird dann 4.7.0 veröffentlicht, usw.

Composer ermöglicht es nun, mit verschiedenen Versionen von Paketen zu arbeiten. Das ist eine gute Sache, aber es birgt das Risiko, dass Entwickler in einem Projekt mit verschiedenen Versionen eines Pakets arbeiten, was wiederum die Möglichkeit von Fehlern eröffnet. Composer ist glücklicherweise für den Umgang mit Versionen konzipiert, wie wir als nächstes sehen werden.

Verfolgung von Projektversionen

Wie geht Composer also mit Versionen um, so dass Entwickler sicherstellen können, dass sie immer die gleichen Paketversionen verwenden? Das geschieht in der composer.lock Datei. Die Datei composer.lock dient im Wesentlichen als Momentaufnahme aller Versionen aller von composer.json verwalteten Pakete. Wenn wir zum Beispiel zum ersten Mal composer require terminal42/notification_center in unserem Projekt ausführen, geschehen ein paar Dinge:

  1. Die aktuelle (neueste) Version vom Notification-Center wird in das Projekt heruntergeladen
  2. Alle Pakete, von denen das Notification-Center abhängt, werden auch in das Projekt heruntergeladen
  3. composer.json wird aktualisiert, um zu zeigen, dass das Notification-Center nun eine Abhängigkeit des Projekts ist
  4. composer.lock wird erstellt bzw. aktualisiert, um die aktuellen Versionen aller vom Composer verwalteten Pakete wiederzugeben

So verfolgt composer.json, welche Pakete verwendet werden und composer.lock ist eine Momentaufnahme der Versionen der Pakete, die derzeit im Projekt verwendet werden.

Composer Install und Composer Update

Das Problem, wenn verschiedene Versionen von Paketen verwenden werden, besteht darin, dass Entwickler Code schreiben können, der nur mit der Version eines Pakets funktioniert, die sie lokal haben und andere Entwickler noch nicht haben. Oder vielleicht verwenden sie eine veraltete Version des Pakets und andere Entwickler haben diese bereits aktualisiert. Composer-Projekte verwalten Paketversionen mit den Befehlen composer install und composer update. Diese Befehle machen verschiedene Dinge, also schauen wir uns als nächstes die Unterschiede zwischen ihnen an.

Wir stellen uns vor, dass Composer keine Versionen verfolgt hat. (Nur als Beispiel! So funktioniert es in echt nicht!).
Die folgende Situation könnte eintreten:

  1. Notification-Center 1.4.4 ist veröffentlicht und ist damit die neuste Version
  2. Entwickler A erstellt ein neues Projekt und setzt das Notification-Center als Abhängigkeit in composer.json. Entwickler A hat nun Notification-Center 1.4.4 in seinem Projekt.
  3. Ein paar Wochen später ist die Version 1.5.0 die aktuellste Version des Notification-Centers
  4. Entwickler B klont das Git-Projekt und installiert die Codebasis mit Hilfe von composer install. Composer lädt das Notification-Center herunter. Entwickler B hat jetzt das Notification-Center 1.5.0.

Die beiden Entwickler arbeiten nun an verschiedenen Versionen des Notification-Centers. Dies ist gefährlich, da jeder Code, den sie schreiben/hinzufügen, möglicherweise nicht mit dem Code des anderen kompatibel ist.

Glücklicherweise kann Composer Paketversionen verfolgen. Wenn ein Benutzer die Composer-Installation ausführt, werden die in composer.lock definierten Versionen installiert. Wenn Developer B also composer install ausführt, wird Notification-Center 1.4.4 installiert, obwohl Notification-Center 1.5.0 die offiziell, aktuellste Version ist. Dies, weil 1.4.4 als Version aufgeführt ist, die vom Projekt in composer.json verwendet wird. Daher sollten Entwickler, die an Composer-verwalteten Projekten arbeiten, die Composer-Installation jedes Mal ausführen, wenn sie Updates aus dem Remote-Git-Repositorys beziehen und sehen, dass die composer.lock geändert hat.

Aktualisieren von Versionen

Wie bereits erwähnt, verfolgt die Datei composer.lock die Versionen der Pakete, die derzeit im Projekt verwendet werden. An dieser Stelle kommt der Befehl composer update ins Spiel. Wir überprüfen, wie man Versionsänderungen für eine bestimmtes Paket verwaltet:

  1. Notification-Center 1.4.4 ist offiziell der aktuellste Stand
  2. Entwickler A erstellt ein neues Projekt und setzt das Notification-Center als Abhängigkeit. Die Datei composer.lock zeichnet die vom Projekt verwendete Version von Notification-Center 1.4.4 auf
  3. Später ist Notification-Center 1.5.0. die aktuellste Version
  4. Entwickler B klont das Git-Projekt und installiert die Codebasis mit Hilfe von composer install. Die composer.lock Datei listet die Version des Notification-Centers, die im Projekt verwendet wird, als 1.4.4 auf, so dass diese Version heruntergeladen wird
  5. Entwickler A sieht, dass eine neue Version des Notification-Centers veröffentlicht wurde. Entwickler A führt nun composer update terminal42/notification_center aus. Composer installiert Notification-Center 1.5.0 im Projekt und aktualisiert composer.lock, um diese Version des Notification-Centers anzuzeigen
  6. Entwickler A überträgt diese aktualisierte composer.lock auf Git und pushed sie in das Remote-Repository
  7. Entwickler B zieht das Git-Repository und erhält die aktualisierte composer.lock Datei. Entwickler B führt dann die Composer-Installation durch und da die als verwendet registrierte Version des Notification-Centers jetzt 1.5.0 ist, aktualisiert Composer den Code bei Entwickler B ebenfalls auf die Version 1.5.0

Jetzt haben Entwickler A und Entwickler B beide genau die gleichen Versionen des Notification-Centers in ihrem Projekt.

Zusammenfassung

Man sollte composer install immer ausführen, wenn man sieht, dass ein Änderungen in der composer.lock Datei vorgenommen wurde, um sicherzustellen, dass man sich auf der gleichen Codebasis wie alle anderen Entwickler befinden.

Man sollten composer install auch immer ausführen, wenn die Git-Branches wechseln, zum Beispiel zwischen einem Produktions- und einem Staging-Branch. Die Abhängigkeiten dieser Branches können sehr unterschiedlich sein und wenn man die Composer-Installation ausführt, werden alle Abhängigkeiten aktualisiert, um sie an den aktuellen composer.lock Version anzupassen.

Der Befehl composer update sollte nur verwendet werden, um auf neue Versionen von Paketen zu aktualisieren. Die Datei composer.lock sollte danach immer sofort ins Git-Repository übertragen werden.


Folgendes sollte nun klar sein

  • Was die composer.json Datei bewirkt
  • Was die composer.lock Datei bewirkt
  • Wann man composer install verwenden sollte
  • Wann Sie das composer update verwenden sollten
  • Wie Git und Composer miteinander interagieren

 

Weiter zu Teil 2: Composer verstehen - Teil 2 - die wichtigsten Befehle

 

Zurück

Kommentare

Kommentar von Max |

Vielen Dank für diese ausführliche Erklärung. Composer ist mir jetzt einiges klarer geworden :-)

Kommentar von Christoph |

Hintergrundwissen verständlich, beispielhaft erklärt, vielen Dank dafür!

Einen Kommentar schreiben

Was ist die Summe aus 4 und 1?