Cover Image

Nach langer Zeit scheibe ich mal wieder einen Blogpost zum Thema PHP. Es geht um das Tool box, was ich gerade für n98-magerun2 eingebaut habe. Das Tool hilft beim Umgang mit phar Dateien. In erster Linie nutze ich es um die n98-magerun2.phar Datei zu erstellen. Bisher habe ich dafür einen phing Task verwendet. Dieser musste allerdings angepasst werden um mit den etwas größeren (> 6 MB) phar Dateien umgehen zu können. In der Vergangenheit hatte ich es schon mal mit box probiert. Damals kam box auch nicht mit den großen Dateien zurecht. Das ist alledings schon ein paar Jahre here (zwischendurch gabe es eine Version 2 und dann einen Fork des Tools). Inzwischen funktioniert die Erstellung aber stabil.

Phar

Phar (PHP Archive) ist eine Erweiterung in PHP, die es ermöglicht, aus einer komprimierten Archivdatei heraus Programme oder Dateien zu verarbeiten, ähnlich Java Archive. Für die Komprimierung der Daten selbst stehen verschiedene Kompressionsverfahren zur Verfügung, z. B. bzip2, gzip oder ZIP.

https://en.wikipedia.org/wiki/PHAR_(file_format)

Installation

Am einfachten ist es, wenn man die box.phar Datei (ja es kommt selbst auch als ausführbare phar Datei) herunterlädt.

Das kann z.B so erfolgen:

curl -L -O https://github.com/box-project/box/releases/download/3.14.0/box.phar
chmod +x ./box.phar

Das war es schon. Man kann jetzt einfach die ./box.phar Datei ausführen und sollte dann sehen was das Tool so anbietet.

Wenn alles funktoniert, sollte man die folgende Ausgabe auf seiner Konsole sehen.

CLI Ausgabe box.phar

Phar “compile”

Die Hauptaufgabe von box ist die Erstellung von phar Archiven. Das Kommando compile ist dafür zuständig. Phar Dateien können ganz unterschiedlich sein. So unterstützt der Standard z.B. unterschiedliche Komprimierungen um die Datei klein zu bekommen. Die phar Dateien können über verschiedene Algorithmen signiert werden. Und noch einige andere Funktionen … Alle Einstellungen können bequem in einer .box.json.dist Datei hinterlegt werden.

Im Falle von n98-magerun2 sieht das z.B. so aus:

{
    "compression": "GZ",
    "algorithm": "SHA512",
    "datetime": "release-date",
    "files": [
        "config.yaml",
        "src/bootstrap.php"
    ],
    "force-autodiscovery": true,
    "directories": [
        "src",
        "res",
        "vendor/twig/twig/src"
    ],
    "git-commit-short": "git_commit_short",
    "stub": "build/phar/_cli_stub.php",
    "output": "n98-magerun2.phar"
}

Wir ihr seht komprimiere ich meine Dateien mit Gzip. Sobald die phar Datei erzeugt wird, werden alle Dateien in der phar automatisch komprimiert. Die phar Datei kann dann nur in PHP Versionen ausgeführt die die zlib Bibliothek eingebunden haben. Das sollte aber bei jedem Standard PHP der Fall sein. Box gibt in dem Fall eine Warnung beim “compile” aus. Zum signieren habe wir uns bei n98-magerun2 für den SHA-512 Algorithmus engschieden. Box macht einige Dinge auch schon selbst, wenn man diese nicht abschaltet. So wird die composer.json, die bei fast jedem PHP Projekt vorhanden ist, automatisch als Basis genommen um die benötigten Dateien eurer Anwendung zu ermitteln. Es wird dann ein optimierter Autoloader mit Classmap generiert und es werden alle Dev-Abhängigkeiten entfernt um die Datei möglichst klein zu halten. Zudem wird bei Composer die Option --classmap-authoritative mitgegeben. Diese sorgt dafür, dass nur die Classmap für das Autoloading verwendet wird. Das ist bei den meisten Anwendungen auch die Best Practice. Im Falle von n98-magerun2 unterstützen wir aber das hinzufügen von Kommandos über externe Module, welche dann den Autoloader zur Laufzeit erweitern. Da man die Option bei box nicht deaktivieren kann, musste ich diese zur Laufzeit deaktivieren. So findet sich jetzt im Magerun Code folgende Zeile:

Config.php

$autoloader->setClassMapAuthoritative(false);

Zusätzliche Dateien, die nicht vom Composer Autoloader geladen werden, können zusätzlich in der Konfiguration angegeben werden. In meinem Fall sind das die config.yaml Datei das res Verzeichnis.

Isolation

Box kann über das Tool php-scoper auch dafür sorgen, dass Namespaces von abhängigen Paketen in der Phar Datei einen Scope Prefix bekommen. Das machen alle möglichen bekannten PHP Projekte wie phstan oder PHPUnit. Das hat den Vorteil, dass man nicht so schnell Konflikte bekommt. Ich selbst habe lange damit getestet da ich bei meinem Tool Symfony Komponenten nutze und gleichzeitig Magento 2 bootstrappe. Da kann es schnell zu einem Komflikt kommen, wenn zweimal die gleiche Symfony Komponente in unterschiedlichen Versionen genutzt wird. Der Scoper funktuniert sehr gut, solange ein PSR-4 Autoloader im Spiel ist. Autoloading über “file” macht dagegen Probleme. In meinem Fall musste ich den Scoper aber wieder ausbauen, da sonst alle Drittmodule auf einem schlag inkompatibel geworden wären da diese dann auch den Namespace-Prefix nutzen müssten. Wenn man keine Konfiguration vornimmt, wird dieser sogar mit einem Zufallswert ermittelt. Alle Einstellungen des php-scoper können dann in einer extra Konfigurtionsdatei scoper.inc.php vorgenommen werden.

Was ist drin in der phar Datei?

Wer mal in die generierte Phar Datei schauen möchte, der wird das extract Kommando lieben. Damit kann jede Phar Datei mal eben entpackt werden.

Hier ein Beispiel, indem die n98-magerun2 Phar Datei in das Verzeichnis extract1 entpackt wird.

./box.phar extract ./n98-magerun2.phar extract1

Das Vergleichen von zwei phar Dateien ist auch möglich. Dafür gibt es den praktische diff Befehl.

./box.phar verify ./n98-magerun2.phar ./latest.phar

Timestamps

Bei jedem erstellen einer Phar Datei wird automatisch ein Timestamp mit der aktuelle Zeit in die Phar Datei geschrieben. Das ist in machen Fällen praktisch. Wir finden es aber gut, wenn jeder Versionstand (GIT-Tag) immer zur gleichen Phar Datei mit gleicher Prüfsumme führt. Das Composer Projekt möchte das auch und hat dazu eine kleine Bibliothek bereitgestellt um den Timestamp nachträglich zu manipulieren. Wir nutzen diese Bibliotken auch bei uns. Wer es genauer wissen will, kann sich die build.sh genauer anschauen. Da der Befehl den binären Teil der Phar Datei manipuliert, sollte man die Phar Datei nochmal verifizieren. Wenn etwas schief läuft, wäre die Signatur inkorrekt und die Phar Datei nicht ausführbar. Es würde dann von PHP eine entsprechende Fehlermeldung kommen, dass die Signatur nicht passt.

Box bietet hier das Kommando verify an.

./box.phar verify ./n98-magerun2.phar

Wenn alles passt, bekommt ihr die Meldung The PHAR passed verification. gefolgt von der SHA-512 Signatur).

Was gibt es sonst noch?

Das war noch lange nicht alles was Box anbietet. So können Dateien vor der Ablage in der Phar nochmal über sogenannte Compactor manipuliert werden. Der bereit beschriebe PHP-Scoper ist auch ein solcher Compactor. Es ist auch mögliche eigene Compactor zu registriren.

Praktisch ist auch das automatische Ersetzen von Platzhaltern. In meinem Fall wird der GIT Commit Hash des genutzten Code-Versionsstands automatisch in den Code geschrieben.

Das n98-magerun2 version Kommando gibt dann z.B folgende Ausgabe aus:

n98-magerun2 4.10.0-dev (commit: 61ce922) by netz98 GmbH

Damit weiß man immer direkt was für ein Commit die Basis der Phar Datei war. Das ist gerade für die “Develop-Version” sehr praktisch.

Auch Docker-Container können mit Box generiert werden. Diese Funktion habe ich aber selbst noch nicht ausprobiert.

Ich hoffe ich konnte mit meinem Blog-Post einen kleine Einblick in das Tool geben und ihr könne es für eigene Projekte gebrauchen.