Neue Konsolen-App my-cli erstellen
composer create-project symfony/skeleton my-cli "5.3.*" cd my-cli bin/console -VVoila, eine neue CLI! Nichts besonderes bisher. Bitte versuche nicht, deine vorhandene CLI in ein PHAR zu packen, bevor du nicht zumindest einmal erfolgreich eine neue, unveränderte CLI in ein PHAR gepackt hast. Sei gewarnt: andere symfony-Versionen haben andere Hürden, die man überwinden muss. Und die Probleme, die du beim Schreiben deiner vorhandenen CLI selbst geschaffen hast, kommen da noch oben drauf!
Aus my-cli ein PHAR machen
Entwicklungs- von Laufzeit-Abhängigkeiten trennen
composer require --dev bamarni/composer-bin-pluginOK, fast perfekt. Das Plugin selbst wird Teil des PHARs sein. Da es aber selbst sehr klein ist, im Verhältnis zu PHAR-Builder und anderen Entwicklertools, kann man damit gut leben. Nun können wir den Entwickler-Kram ausserhalb der Projekt-/composer.json einbinden und die Dateien landen in einem Extra-Verzeichnis /vendor-bin/. Das Konzept des Plugins erlaubt es, für verschiedene Tools verschiedene benannte „Umgebungen” zu erstellen. Bevor wir das tun, erleichtern wir uns die künftige Handhabung mit ein paar Automatismen für Installation und Update. Das Verlinken der Tools nach /vendor/bin stellen wir dabei auch ab, da es sehr oft Konflikte erzeugt. Ersetze die Sektion „scripts” in der generierten /composer.json durch die folgende und füge den Eintrag zu „extra” hinzu:
"scripts": { "auto-scripts": [ ], "post-install-cmd": [ "@cache:drop", "@composer bin all install --ansi" ], "post-update-cmd": [ "@cache:drop", "@composer bin all update --ansi" ], "cache:drop": [ "[ ! -d var ] || rm -rf var" ] }, "extra": { "bamarni-bin": { "bin-links": false } },Es ist Absicht, dass die „auto-scripts” leer sind. Ohne den Eintrag läuft symfony/flex nicht mehr und verhindert die composer-Benutzung. Mit den dort möglichen Kommando-Referenzen kann dann aber wiederum der PHAR-Builder nicht erfolgreich ausgeführt werden. So wie oben geht nun alles wie benötigt.
Den PHAR-Builder in isolierte Umgebung installieren
composer bin box config minimum-stability dev composer bin box require --dev humbug/box '^3.13.0'Das neu erzeugte /vendor-bin/-Verzeichnis enthält ein paar Dateien, die man aufheben sollte, und die nicht zum Aufheben gedachten Abhängigkeiten der Tools. Ergänze die /.gitignore im Projekt-Root, um die Abhängigkeiten nicht in git zu versionieren, die nötigen /vendor-bin/**/composer.* aber schon. Und für das Arbeitsverzeichnis des PHAR-Builders fügen wir auch gleich einen Eintrag hinzu:
... ###> Isolated dev tools ### /vendor-bin/**/vendor ###< Isolated dev tools ### ###> Box PHAR generator ### /.box_dump/ ###< Box PHAR generator ###Erstelle nun noch eine /box.json in deinem Projekt-Root, um dem PHAR-Builder eine zur symfony CLI passende Grundkonfiguration zu geben:
{ "directories": [ "config", "src", "var/cache/prod" ], "files": [ ".env.local.php" ], "finder": [ { "name": "*.php", "exclude": ["Tests"], "in": "vendor" } ], "git-version": "application_version", "main": "bin/console", "output": "build/console", "dump-autoload": false }
Die CLI für die PHAR-Nutzung anpassen
... class Kernel extends BaseKernel { use MicroKernelTrait; // In PHAR, Kernel cannot auto-detect this path public function getProjectDir(): string { return \dirname(__DIR__); } protected function configureContainer(ContainerConfigurator $container): void ...Die mit symfony 5.3 neu eingeführte runtime-Komponente macht ebenfalls Probleme. Hier versucht symfony, mit $_SERVER[‚SCRIPT_FILENAME‘] zu arbeiten. Diese Variable enthält das aufgerufene Skript, also die PHAR-Datei selbst. symfony benötigt dort aber den Pfad der aufgerufenen PHP-Datei, um diese zu includieren. Das reparieren wir in bin/console:
.... // In PHAR, $_SERVER['SCRIPT_FILENAME'] is the PHAR itself. symfony runtime // needs it to point to current file to include it: $_SERVER['SCRIPT_FILENAME'] = __FILE__; require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; return function (array $context) { ...Überprüfe nun, ob my-cli nach den Änderungen noch funktioniert:
bin/console -V
PHAR-Build-Prozess
git init --initial-branch=main git add . git commit -m "Base commit"Im PHAR muss symfony im prod-Modus laufen. Und es kann den nötigen Cache für die Dependency-Injection nicht im PHAR selbst erzeugen. Wir müssen das also vorab machen, damit wir alles mit ins PHAR reinpacken können:
composer dump-env prod bin/console cache:clearDamit haben wir nun das prod-Environment vorkompiliert in die Datei /env.local.php und den Cache vorgewärmt in /var/cache/prod. Beide Pfade sind in der /box.json zum Einfügen ins PHAR konfiguriert. OK, erstellen wir das PHAR und stellen symfony zurück in den dev-Modus:
vendor-bin/box/vendor/bin/box compile rm .env.local.php build/console -VHurra! Die CLI wurde zum ersten Mal aus dem PHAR ausgeführt! Damit du die Build-Befehle nicht ständig wiederholen musst, füge sie als Skript build:phar in die /composer.json ein:
"scripts": { "build:phar": [ "@composer dump-env prod", "bin/console cache:clear", "vendor-bin/box/vendor/bin/box compile", "rm .env.local.php" ] }Ab jetzt kannst die eine neue PHAR-Version mit nur einem Kommando erstellen:
composer build:phar build/console -VUnd wo wir gerade von „Version“ reden, als Vorbereitung für den nächsten Teil:
git add . git commit -m "PHARize CLI app" git tag 0.0.1 -m '0.0.1'
Ein bischen PHAR-Builder-Magie
... use Symfony\Bundle\FrameworkBundle\Console\Application; define('APP_NAME', 'My CLI application'); define('APP_VERSION', '@application_version@'); if (!is_file(dirname(__DIR__).'/vendor/autoload_runtime.php')) { ... return function (array $context) { $kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); $application = new Application($kernel); $application->setName(APP_NAME); $application->setVersion(APP_VERSION); return $application; };Das war es auch schon. Einmal neu erstellen:
bin/console -V My CLI application @application_version@ (env: dev, debug: true) composer build:phar build/console -V My CLI application 0.0.1 (env: prod, debug: false)Magie! Da passiert sie!
git add . git commit -m "Add version to CLI" git tag 0.0.2 -m '0.0.2' composer build:phar build/console -V My CLI application 0.0.2 (env: prod, debug: false)
Ein Kommando der CLI hinzufügen
<?php declare(strict_types=1); namespace App\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class HelloWorldCommand extends Command { protected static $defaultName = 'hello:world'; protected function configure(): void { $this ->setDescription('Say Hello! to the world') ->setHelp('My very first symfony CLI command. Very friendly!') ; } protected function execute(InputInterface $input, OutputInterface $output): int { $output->writeln('Hello, world!'); return Command::SUCCESS; } }
bin/console list bin/console hello:world --help bin/console hello:world git add . git commit -m "Add command to CLI" git tag 0.0.3 -m '0.0.3' composer build:phar build/console list build/console hello:world --help build/console hello:world
Den Namespace App in My\Cli umbenennen
bin/console hello:world git add . git commit -m "Use namespace My\Cli for app" git tag 0.0.4 -m '0.0.4' composer build:phar build/console hello:worldEs funktioniert hoffentlich noch alles. Dann weiter…
Das Binary console in my-cli umbenennen
bin/my-cli -V git add . git commit -m "Rename CLI to my-cli" git tag 0.0.5 -m '0.0.5' composer build:phar build/my-cli -V
Weitere Hinweise zu PHARs
Mach´s einfach digital