Composer Lock Datei und reproduzierbare Builds: Hashes, Plattform Config, CI
AI generated
<?php
8.4
PHP · Composer · Paket Ökosystem
Composer Lock Datei und reproduzierbare Builds
warum composer.lock immer ins Repository gehört

Ein Deployment, das auf dem Entwickler Rechner funktioniert, aber in Produktion mit einer anderen Paketversion fehlschlägt, ist meistens auf eine fehlende oder falsch behandelte composer.lock Datei zurückzuführen. Wer versteht, wie Composer Hashes bildet, wie platform Config die Auflösung beeinflusst und wie Merge Konflikte in der Lock Datei entstehen, baut PHP Anwendungen, die in jeder Umgebung exakt dieselben Paketversionen installieren.

18 Min. Lesezeit composer.lock · Hash Verifikation · platform Config PHP 8.4 · Composer 2.x

1. Warum composer.lock existiert und was sie garantiert

Die composer.lock Datei löst ein grundlegendes Problem, das jede Versionsbedingung in composer.json zwangsläufig mit sich bringt: eine Angabe wie mironsoft/core ^2.0 lässt jede Version von 2.0.0 bis ausschließlich 3.0.0 zu. Ohne eine Lock Datei würde composer install bei jedem Aufruf erneut die jeweils aktuellste passende Version aus dieser Bandbreite auflösen, was bedeutet, dass ein Entwickler heute Version 2.3.1 installiert, ein Kollege morgen aber bereits 2.4.0, ohne dass sich composer.json auch nur geändert hätte.

Die composer.lock Datei fixiert genau diese Auflösung: sie enthält für jede installierte Abhängigkeit die exakte Version, den exakten Commit Hash und einen Prüfsummen Hash, sodass composer install, sobald eine Lock Datei vorhanden ist, niemals erneut die Versionsauflösung durchführt, sondern ausschließlich die dort fixierten Versionen installiert. Erst composer update ignoriert die bestehende Lock Datei bewusst und berechnet eine neue Auflösung basierend auf den aktuellen Versionsbedingungen in composer.json.

Diese Unterscheidung ist der Kern von reproduzierbaren Builds in PHP: composer install mit einer vorhandenen Lock Datei liefert auf einem Entwickler Laptop, einem CI Runner und einem Produktionsserver garantiert dieselben Paketversionen, unabhängig davon, wann und wo der Befehl ausgeführt wird. Ohne diese Garantie wäre jedes Deployment ein Glücksspiel, bei dem eine zwischenzeitlich veröffentlichte, inkompatible Patch Version einer Abhängigkeit unbemerkt in Produktion landen könnte.

2. Struktur der Lock Datei: was tatsächlich gespeichert wird

Eine composer.lock Datei ist reines JSON und enthält im Kern zwei Arrays: packages für Produktionsabhängigkeiten und packages-dev für Entwicklungsabhängigkeiten. Jeder Eintrag darin speichert nicht nur Name und Version, sondern auch die exakte Quelle, meist ein Git Repository mit konkretem Commit Hash, sowie den dist Download Ort, üblicherweise ein von Packagist bereitgestelltes Zip Archiv des jeweiligen Tags.

Zusätzlich enthält die Lock Datei ein Feld platform, das die zur Auflösungszeit relevanten PHP Version und Extension Anforderungen dokumentiert, sowie ein plugin-api-version Feld, das die Composer Version zum Zeitpunkt der letzten Aktualisierung festhält. Diese Metadaten erklären, warum eine sehr alte Lock Datei, die mit einer deutlich älteren Composer Version erzeugt wurde, gelegentlich Warnungen beim ersten composer install mit einer neueren Composer Version erzeugt.


{
  "_readme": [
    "This file locks the dependencies of your project to a known state",
    "read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
  ],
  "content-hash": "b1946ac92492d2347c6235b4d2611184",
  "packages": [
    {
      "name": "mironsoft/core",
      "version": "2.3.1",
      "source": {
        "type": "git",
        "url": "https://github.com/mironsoft/core.git",
        "reference": "a1b2c3d4e5f6789012345678901234567890abcd"
      },
      "dist": {
        "type": "zip",
        "url": "https://api.github.com/repos/mironsoft/core/zipball/a1b2c3d4e5f6789012345678901234567890abcd"
      }
    }
  ],
  "platform": {
    "php": "^8.4"
  },
  "platform-dev": [],
  "plugin-api-version": "2.6.0"
}

3. Hash Verifikation: content hash und package hash

Der content-hash am Anfang jeder composer.lock Datei ist eine Prüfsumme über die relevanten Teile der composer.json, insbesondere require, require-dev und die Versionsbedingungen. Composer vergleicht diesen Hash bei jedem Aufruf mit einer frisch berechneten Prüfsumme der aktuellen composer.json. Weichen beide voneinander ab, meldet Composer, dass die Lock Datei nicht mehr aktuell ist und ein composer update erforderlich sein könnte, bevor composer install ohne Warnung fortfährt.

Zusätzlich zum content-hash prüft Composer beim Download jedes einzelnen Pakets dessen Integrität über einen im Lock Eintrag hinterlegten SHA Hash des Archivs. Diese Prüfung findet unabhängig davon statt, ob die Quelle Packagist, ein privates Registry oder ein Path Repository ist, und verhindert, dass ein manipuliertes oder beschädigtes Archiv unbemerkt installiert wird. Ein Hash Mismatch führt zu einem harten Fehlschlag von composer install, nicht zu einer stillen Fortsetzung mit potenziell kompromittiertem Code.


# Verify that composer.lock is in sync with composer.json without installing anything
composer validate --no-check-publish --strict

# Explicitly check whether the lock file's content-hash matches composer.json
composer install --dry-run

# If the hash mismatches, Composer prints a warning like:
# Warning: The lock file is not up to date with the latest changes in composer.json.

4. Platform Config: PHP Version und Extensions festnageln

Ein subtiler, aber praxisrelevanter Aspekt reproduzierbarer Builds ist die platform Konfiguration in composer.json, nicht zu verwechseln mit dem platform Feld in der Lock Datei selbst. Über config.platform.php kann eine bestimmte PHP Version erzwungen werden, unabhängig davon, welche PHP Version auf dem Rechner tatsächlich installiert ist, auf dem composer update ausgeführt wird. Das ist relevant, wenn ein Entwickler lokal eine neuere PHP Version nutzt als der Produktionsserver.

Ohne eine explizite platform Konfiguration löst Composer Abhängigkeiten anhand der lokal tatsächlich installierten PHP Version und Extensions auf. Löst ein Entwickler mit PHP 8.4 lokal eine neue Abhängigkeit auf, die eine erst in PHP 8.3 eingeführte Funktion nutzt, während der Produktionsserver noch PHP 8.2 betreibt, schlägt composer install in Produktion unerwartet fehl, selbst mit identischer composer.lock Datei. Eine explizite platform Angabe stellt sicher, dass Composer bei jeder Auflösung dieselbe Ziel PHP Version zugrunde legt, unabhängig von der lokal installierten Version.


{
  "config": {
    "platform": {
      "php": "8.2.99",
      "ext-redis": "6.0.2"
    },
    "sort-packages": true
  }
}

5. composer install versus composer update im Deployment

In jedem Deployment Skript gehört ausschließlich composer install --no-dev --optimize-autoloader in den produktiven Pfad, niemals composer update. Der Grund liegt direkt im Zweck reproduzierbarer Builds: composer install liest ausschließlich die bestehende composer.lock Datei und installiert exakt die dort fixierten Versionen, während composer update bewusst eine komplett neue Versionsauflösung durchführt und dabei unbeabsichtigt eine neue, in Produktion ungetestete Paketversion einspielen kann.

Die Option --no-dev überspringt zusätzlich sämtliche packages-dev Einträge wie PHPUnit oder PHPStan, die in Produktion weder benötigt werden noch dort vorhanden sein sollten, aus Sicherheitsgründen ebenso wie zur Reduktion der Installationsgröße. --optimize-autoloader generiert eine Klassenkarte statt der Standard PSR-4 Verzeichnis Suche, was in Produktion messbar schnellere Autoloading Zeiten bedeutet, insbesondere bei Projekten mit vielen hundert Klassen.


# Deployment step — reproducible, no dependency resolution happens here
composer install --no-dev --optimize-autoloader --no-interaction --no-progress

# NEVER run this in a deployment pipeline, it re-resolves versions:
# composer update --no-dev

# Verify the lock file matches composer.json before deploying (fails the build if not)
composer validate --strict

6. Merge Konflikte in composer.lock lösen

Merge Konflikte in der composer.lock Datei entstehen zwangsläufig, sobald zwei Feature Branches gleichzeitig unterschiedliche Abhängigkeiten hinzufügen oder aktualisieren. Weil die Datei JSON mit tief verschachtelten Arrays ist, führt ein manuelles Zusammenführen des Git Konflikt Markers fast immer zu einer syntaktisch ungültigen oder inhaltlich inkonsistenten Datei, selbst wenn der Merge auf den ersten Blick sauber aussieht.

Die korrekte Vorgehensweise ist, composer.lock niemals von Hand zu mergen. Stattdessen wird composer.json aus beiden Branches sauber zusammengeführt, die composer.lock Datei komplett verworfen, und mit composer update --lock, das ausschließlich die Lock Datei neu berechnet ohne tatsächlich Pakete zu installieren, eine frische, konsistente Lock Datei erzeugt. Dieser Ansatz vermeidet jede Form von manueller JSON Bearbeitung an einer Datei, die ohnehin als generiertes Artefakt behandelt werden sollte.


# When composer.lock has merge conflicts after resolving composer.json manually:
git checkout --theirs composer.json   # or manually merge composer.json requirements
rm composer.lock

# Regenerate the lock file only, without installing packages
composer update --lock

git add composer.json composer.lock
git commit -m "Resolve composer.lock conflict by regenerating"

7. Reproduzierbarkeit in CI Pipelines absichern

Eine CI Pipeline sollte den Build sofort abbrechen, wenn composer.json und composer.lock nicht mehr zueinander passen, statt stillschweigend eine veraltete Lock Datei zu verwenden oder unbemerkt eine neue Auflösung durchzuführen. composer install --no-dev in Kombination mit vorausgehendem composer validate --strict deckt diesen Fall zuverlässig auf, noch bevor der eigentliche Installationsschritt beginnt.

Für reproduzierbare Builds über mehrere CI Läufe hinweg empfiehlt sich zusätzlich das Cachen des Composer Download Verzeichnisses, nicht des vendor Verzeichnisses selbst, basierend auf einem Hash der composer.lock Datei als Cache Key. Ändert sich composer.lock nicht, liefert der Cache identische Downloads ohne erneuten Netzwerkzugriff auf Packagist, was Build Zeiten spürbar verkürzt, ohne die Reproduzierbarkeit im Vergleich zu einem frischen composer install zu beeinträchtigen.

8. composer.lock versionieren: Ausnahmen und Sonderfälle

Für Anwendungen gilt die klare Regel: die composer.lock Datei gehört immer ins Git Repository, ohne Ausnahme. Nur so garantiert composer install auf jedem Rechner dieselben Paketversionen. Eine verbreitete, aber falsche Annahme ist, composer.lock in .gitignore aufzunehmen, um Merge Konflikte zu vermeiden. Das Ergebnis ist genau das Gegenteil des gewünschten Effekts: ohne versionierte Lock Datei installiert jeder Entwickler und jeder CI Lauf potenziell unterschiedliche Paketversionen, exakt das Problem, das composer.lock lösen soll.

Die einzige dokumentierte Ausnahme betrifft reine Bibliotheken, also Composer Pakete vom Typ library, die von anderen Projekten als Abhängigkeit eingebunden werden. Für Bibliotheken empfiehlt die offizielle Composer Dokumentation, composer.lock nicht zu versionieren, weil die tatsächlich verwendeten Abhängigkeitsversionen ohnehin vom konsumierenden Projekt bestimmt werden und eine Lock Datei der Bibliothek selbst keine praktische Wirkung entfaltet. Für Anwendungen, die tatsächlich deployt werden, bleibt die Versionierung der Lock Datei jedoch zwingend.

9. composer install vs. composer update im direkten Vergleich

Die folgende Tabelle fasst zusammen, wann welcher Befehl zum Einsatz kommt und welche Auswirkung er auf reproduzierbare Builds hat.

Aspekt composer update composer install Praxisrelevanz
Liest composer.lock Ignoriert bestehende Lock Datei Nutzt ausschließlich die Lock Datei Entscheidend für Reproduzierbarkeit
Versionsauflösung Neue Auflösung bei jedem Aufruf Keine erneute Auflösung Verhindert unbeabsichtigte Updates
Einsatzort Lokal, gezielt bei Bedarf CI, Deployment, Produktion Deployment nutzt immer install
Aktualisiert composer.lock Ja, schreibt neue Lock Datei Nein, Lock Datei bleibt unverändert Kontrollierte Updates nötig
Geschwindigkeit Langsamer, komplette Auflösung Schneller, keine SAT Solver Berechnung Relevant für CI Laufzeit

In der Praxis bedeutet diese Tabelle eine klare Trennung der Verantwortlichkeiten: composer update ist ein bewusster, überwachter Vorgang, meist lokal von einem Entwickler ausgelöst und in einem eigenen Pull Request review, während composer install der einzige Befehl ist, der jemals in einer CI Pipeline oder einem Deployment Skript auftauchen sollte, um reproduzierbare Builds über alle Umgebungen hinweg zu garantieren.

Mironsoft

PHP Architektur, Deployment Strategie und Composer Tooling

Deployments, die in jeder Umgebung dieselben Paketversionen installieren?

Wir prüfen eure Deployment Pipeline auf riskante composer update Aufrufe, richten platform Config und Hash Validierung ein und sorgen für konsistente, reproduzierbare Builds von lokal bis Produktion.

Pipeline Audit

Analyse eurer bestehenden CI und Deployment Skripte auf Reproduzierbarkeit

Konfiguration

Platform Config, Hash Validierung und Merge Konflikt Workflow sauber eingerichtet

CI Optimierung

Caching Strategie für Composer Downloads ohne Reproduzierbarkeit zu gefährden

10. Zusammenfassung

Die composer.lock Datei ist das Fundament reproduzierbarer Builds in PHP: sie fixiert exakte Versionen, Commit Hashes und Prüfsummen für jede Abhängigkeit, sodass composer install in jeder Umgebung garantiert dieselben Pakete installiert. Der content-hash erkennt Abweichungen zwischen composer.json und der Lock Datei, während platform Config in composer.json sicherstellt, dass die Versionsauflösung unabhängig von der lokal installierten PHP Version stets dieselbe Ziel Plattform berücksichtigt.

Für Deployment und CI gilt strikt: composer install --no-dev --optimize-autoloader, niemals composer update. Merge Konflikte in der Lock Datei werden nicht manuell aufgelöst, sondern durch composer update --lock neu generiert, nachdem composer.json sauber zusammengeführt wurde. Und die wichtigste Regel überhaupt: die composer.lock Datei gehört für jede Anwendung ins Git Repository, ohne Ausnahme, nur reine Bibliotheken sind davon ausgenommen.

Composer Lock Datei und reproduzierbare Builds — Das Wichtigste auf einen Blick

Hash Verifikation

content-hash prüft composer.json gegen die Lock Datei, jedes Paket wird zusätzlich über einen Archiv Hash verifiziert.

Platform Config

config.platform.php fixiert die Ziel PHP Version bei der Auflösung, unabhängig von der lokal installierten Version.

Deployment Befehl

composer install --no-dev --optimize-autoloader in Produktion, niemals composer update im Deployment Skript.

Merge Konflikte

composer.lock niemals manuell mergen, stattdessen composer update --lock nach sauberem composer.json Merge.

11. FAQ: Composer Lock Datei und reproduzierbare Builds

1Was macht composer.lock genau?
Sie fixiert Version, Quelle und Prüfsumme jeder Abhängigkeit, damit composer install überall dieselben Paketversionen installiert.
2Was ist der content-hash?
Eine Prüfsumme über relevante Teile von composer.json. Weicht sie ab, meldet Composer einen möglicherweise nötigen Update.
3Warum install statt update im Deployment?
install nutzt nur die Lock Datei, update löst neu auf und kann unbeabsichtigt ungetestete Versionen installieren.
4Wofür dient platform Config?
Erzwingt eine bestimmte PHP Version bei der Auflösung, unabhängig von der lokal installierten Version des Entwicklers.
5Wie löse ich Merge Konflikte?
Niemals manuell, composer.json mergen, composer.lock löschen und mit composer update --lock neu erzeugen.
6Gehört composer.lock immer ins Repository?
Für Anwendungen ja, ohne Ausnahme. Nur reine Bibliotheken versionieren composer.lock üblicherweise nicht.
7Was passiert bei einem Hash Mismatch?
composer install schlägt hart fehl statt still fortzufahren, das schützt vor manipulierten oder beschädigten Archiven.
8Was bewirkt --no-dev?
Überspringt alle Entwicklungsabhängigkeiten wie PHPUnit, die in Produktion weder nötig noch sicher dort vorhanden sein sollten.
9Wie beschleunige ich Installs in CI?
Mit Caching des Download Verzeichnisses über einen Hash von composer.lock als Cache Key, ohne Reproduzierbarkeit zu verlieren.
10Wie prüfe ich Synchronität beider Dateien?
Mit composer validate --strict, das den content-hash prüft und die CI bei Abweichung sofort abbricht.