mitmario.dev

composer.json

PHP Sandbox 4 Min Lesezeit 3 BeispieleLektion 2 von 6

Jedes Composer-Projekt hat genau eine composer.json, und sie liegt ganz oben im Projektordner. Sie ist der Ausweis deines Projekts und zugleich seine Einkaufsliste. Anlegen kannst du sie mit composer init, das dich der Reihe nach fragt, oder mit einem Texteditor. Am Ende ist es JSON, und JSON kannst du seit Lektion 8.3.

In diesem Kurs nimmst du den Editor. Das Terminal hier gibt einem laufenden Programm keine Tastatureingabe (help sagt es), composer init fragt dann nichts und schreibt wortlos eine fast leere Datei über die vorhandene. Der nächste Prüflauf stellt sie wieder her, aber schöner ist es, gar nicht erst dorthin zu kommen.

Sie ist eine ganz gewöhnliche Datei

Die Projektdatei, von PHP gelesen
<?php

// Die composer.json ist ganz gewoehnliches JSON. Wir lesen sie mit
// genau den Mitteln aus Lektion 8.3.

$projekt = json_decode(file_get_contents("composer.json"), true);

echo "Projekt: ", $projekt["name"], "\n";
echo "Zweck:   ", $projekt["description"], "\n";

echo "\nBraucht im Betrieb:\n";
foreach ($projekt["require"] as $paket => $bereich) {
    echo "  ", $paket, " ", $bereich, "\n";
}

echo "\nBraucht nur zum Entwickeln:\n";
foreach ($projekt["require-dev"] as $paket => $bereich) {
    echo "  ", $paket, " ", $bereich, "\n";
}

Bevor wir über die Felder reden, ein Beweis: Das Beispiel liest die composer.json mit file_get_contents und json_decode, genau wie jede andere Datei im letzten Abschnitt. Nichts daran ist magisch. Composer liest dieselbe Datei, nur gründlicher.

Zwei Felder stehen hier nebeneinander, und der Unterschied ist wichtig:

  • require sind die Pakete, die dein Programm im Betrieb braucht. Ohne sie läuft es nicht.
  • require-dev sind die, die nur du beim Entwickeln brauchst. Ein Testwerkzeug gehört hierhin, ein Werkzeug zum Aufräumen von Code auch. Auf dem Server lässt man sie weg, indem man dort composer install --no-dev aufruft, und das ist der Sinn der Trennung: weniger fremder Code auf der Maschine, die im Netz steht.

phpunit/phpunit steht deshalb unter require-dev, und genau dieses Paket kommt in Abschnitt 17 wieder.

Das Zeichen vor der Versionsnummer

Was die Zeichen vor der Version bedeuten
<?php

// Composer entscheidet mit denselben Vergleichen, die PHP eingebaut
// hat. version_compare kennt die Zaehlweise von Versionsnummern.

$kandidaten = ["1.4.1", "1.4.2", "1.4.9", "1.5.0", "1.9.3", "2.0.0"];

printf("%-8s %-8s %-8s %s\n", "Version", "^1.4.2", "~1.4.2", "1.4.*");

foreach ($kandidaten as $version) {
    $caret = version_compare($version, "1.4.2", ">=")
        && version_compare($version, "2.0.0", "<");

    $tilde = version_compare($version, "1.4.2", ">=")
        && version_compare($version, "1.5.0", "<");

    $stern = version_compare($version, "1.4.0", ">=")
        && version_compare($version, "1.5.0", "<");

    printf(
        "%-8s %-8s %-8s %s\n",
        $version,
        $caret ? "ja" : "nein",
        $tilde ? "ja" : "nein",
        $stern ? "ja" : "nein"
    );
}

"^4" heißt nicht „genau Version 4”. Es ist ein Bereich, und welcher, entscheidet die semantische Versionierung. Sie ist eine Absprache unter Paketautoren und geht so: Eine Version besteht aus drei Zahlen, HAUPT.NEBEN.FEHLER.

  • Die Fehlernummer steigt bei einer reinen Fehlerbehebung.
  • Die Nebennummer steigt, wenn etwas dazukommt, das Bestehende aber weiter funktioniert.
  • Die Hauptnummer steigt, wenn etwas kaputtgeht, das vorher funktioniert hat.

Damit kannst du dem Paket sagen, wie mutig es sein darf. Das Beispiel rechnet es mit version_compare() vor, und die Tabelle liest sich am besten von links nach rechts:

  • ^1.4.2 erlaubt alles bis unter die nächste Hauptnummer. 1.9.3 ist dabei, 2.0.0 nicht. Das ist die übliche Angabe, und mit ihr bekommst du Fehlerbehebungen ohne Zutun.
  • ~1.4.2 erlaubt nur bis unter die nächste Nebennummer. 1.4.9 ist dabei, 1.5.0 nicht. Vorsichtiger, und manchmal genau richtig.
  • 1.4.* ist dasselbe wie ~1.4.0, nur anders geschrieben.
  • 1.4.2 ohne Zeichen heißt: genau diese eine Version, keine andere.

Die Absprache ist eine Absprache und kein Gesetz. Sie hält, weil Paketautoren sie ernst nehmen, und sie hält nicht immer. Deshalb gibt es die zweite Datei, um die es gleich geht.

Die ganze Datei auf einen Blick

Eine composer.json von vorn bis hinten
{
    "name": "hofladen/kasse",
    "description": "Kleine Kasse fuer den Hofladen",
    "type": "project",
    "license": "MIT",

    "require": {
        "php": "^8.2",
        "ramsey/uuid": "^4"
    },

    "require-dev": {
        "phpunit/phpunit": "^11"
    },

    "autoload": {
        "psr-4": {
            "Hofladen\\": "src/"
        }
    },

    "scripts": {
        "test": "phpunit",
        "kasse": "php bin/kasse.php"
    }
}

Das ist eine vollständige composer.json, wie sie in einem kleinen Projekt aussieht. Sie läuft nicht, sie ist zum Lesen da. Die Felder der Reihe nach:

  • name ist immer zweiteilig: Hersteller und Projekt, getrennt durch einen Schrägstrich, alles klein. Auch dein eigenes Projekt braucht ihn, selbst wenn du es nie veröffentlichst.
  • description, type und license sind Angaben über dein Projekt. Wer nichts veröffentlicht, kann sie knapp halten.
  • require und require-dev kennst du jetzt.
  • autoload sagt, wo dein eigener Code liegt und unter welchem Namen er zu finden ist. Das ist Lektion 9.5, und dort wird auch klar, was psr-4 bedeutet und warum hinter Hofladen zwei Rückstriche stehen.
  • scripts sind Abkürzungen für Befehle. composer test führt dann aus, was hinter test steht. Praktisch, sobald ein Befehl länger wird als deine Geduld.

Und die zweite Datei daneben

Sobald du zum ersten Mal etwas installierst, legt Composer eine composer.lock an. Sie hält fest, welche Version tatsächlich geholt wurde, auf die Ziffer genau, dazu die Stelle im Git des Pakets, aus der sie stammt, und alles, was die Pakete selbst noch mitgebracht haben. In Lektion 9.3 liegt sie neben deiner Aufgabe, dann kannst du hineinsehen.

Beide Dateien gehören in die Versionsverwaltung, und das ist keine Geschmacksfrage. Die composer.json sagt, was erlaubt wäre. Die composer.lock sagt, was bei dir wirklich läuft, und sorgt dafür, dass auf dem Server exakt dasselbe läuft. Ohne sie steht dort vielleicht 1.9.3, während bei dir 1.4.2 lief, und der Unterschied fällt an einem Freitagabend auf.

vendor/ gehört dagegen nicht dazu. Der Ordner ist das Ergebnis, die beiden Dateien sind das Rezept.

Zum Mitnehmen

Eine einzige Datei sagt, was dein Projekt braucht, wie es heißt und wo dein eigener Code liegt. Wer sie lesen kann, versteht ein fremdes PHP-Projekt in zwei Minuten.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 3 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe, auf dem Server geprüft

    Öffnet sich mit dem Basis Konto.