vom rohen $argv-Skript zum getesteten Tool
Rohe PHP-CLI-Skripte mit $argv stoßen schnell an Grenzen: keine Validierung, kein Hilfetext, keine Tests. Die Symfony-Console-Komponente lässt sich als eigenständiges Composer-Paket in jedem PHP-Projekt einsetzen und liefert Command-Klassen, Prompts, Fortschrittsanzeigen und Exit-Codes als solides Fundament.
Inhaltsverzeichnis
- 1. Warum rohe PHP-CLI-Skripte mit $argv an Grenzen stoßen
- 2. Grundgerüst mit der Symfony-Console-Komponente
- 3. Argumente und Optionen definieren
- 4. Interaktive Prompts und Validierung mit SymfonyStyle
- 5. Fortschrittsanzeigen und Tabellen
- 6. Exit-Codes und robuste Fehlerbehandlung
- 7. Commands testen mit CommandTester
- 8. Scheduling und Locking
- 9. Von Skript zu Tool: Packaging als PHAR
- 10. Zusammenfassung
- 11. FAQ
1. Warum rohe PHP-CLI-Skripte mit $argv an Grenzen stoßen
Fast jedes PHP-Projekt beginnt seine CLI-Historie mit einem einfachen Skript, das $argv von Hand auswertet: $argv[1] ist der Dateiname, $argv[2] vielleicht eine Umgebung, $argv[3] ein Flag. Das funktioniert für den ersten Anwendungsfall, wird aber mit jedem zusätzlichen Parameter fragiler. Es gibt keine eingebaute Validierung, ob ein Pflichtargument überhaupt übergeben wurde, keine Typprüfung, keine sinnvolle Fehlermeldung, wenn die Reihenfolge der Parameter nicht stimmt. Der Aufrufer muss die interne Struktur des Skripts kennen, weil nichts davon dokumentiert ist.
Ein zweites, oft unterschätztes Problem ist der fehlende Hilfetext. Wer ein rohes $argv-Skript nach sechs Monaten wieder aufruft, findet üblicherweise keine --help-Option, sondern muss den Quellcode öffnen, um herauszufinden, welche Parameter erwartet werden und in welcher Reihenfolge. Auch Dinge wie farbige Ausgabe für Erfolg und Fehler, saubere Exit-Codes für Monitoring-Systeme oder interaktive Rückfragen bei fehlenden Angaben müssen komplett von Hand nachgebaut werden. Jedes Skript im Projekt erfindet dabei seine eigenen, leicht unterschiedlichen Konventionen.
Am schwersten wiegt aber die fehlende Testbarkeit. Ein $argv-Skript lässt sich nur schwer isoliert testen, weil Ein- und Ausgabe eng mit den PHP-Superglobalen und mit echo-Aufrufen verwoben sind. Die Symfony Console-Komponente löst genau diese Probleme, ohne dass das komplette Symfony-Framework installiert werden muss. Als eigenständiges Composer-Paket bringt sie Command-Klassen, Argument-Validierung, Hilfetext, Prompts und einen Test-Helper mit, der CLI-Logik ohne echten Terminal-Aufruf prüfbar macht.
| Merkmal | Rohes PHP-Skript ($argv) | Symfony Console |
|---|---|---|
| Argument-Validierung | manuell, fehleranfällig | InputArgument, automatisch geprüft |
| Hilfetext (--help) | nicht vorhanden | automatisch generiert |
| Farbige Ausgabe | manuelle ANSI-Codes | SymfonyStyle, eingebaut |
| Testbarkeit | kaum isolierbar | CommandTester, PHPUnit-tauglich |
| Exit-Codes | von Hand mit exit() | Command::SUCCESS/FAILURE/INVALID |
| Interaktive Prompts | eigene readline-Logik | QuestionHelper, Validierung inklusive |
| Auto-Completion | nicht vorhanden | CompletionInput je Command |
2. Grundgerüst mit der Symfony-Console-Komponente
Der Einstieg in Symfony Console gelingt mit einer einzigen Composer-Abhängigkeit: composer require symfony/console. Damit ist keine Kopplung an ein bestehendes Framework nötig, das Paket lässt sich in jedem PHP-8.4-Projekt einsetzen, ob mit Symfony, Laravel oder komplett ohne Framework. Zentrales Objekt ist die Application-Klasse, die als Container für alle registrierten Commands dient und das eigentliche Parsen der Kommandozeile übernimmt.
Ein einzelner Befehl wird als eigene Klasse modelliert, die von Symfony\Component\Console\Command\Command erbt. Die Methode configure() beschreibt Name, Beschreibung und alle Argumente und Optionen des Commands, während execute() die eigentliche Logik enthält und einen Exit-Code zurückgibt. Diese klare Trennung zwischen Deklaration und Ausführung macht Commands sofort lesbar, auch ohne die restliche Codebasis zu kennen.
Wichtig für CLI-Skripte in produktiven Projekten ist außerdem, dass eine Application beliebig viele Commands verwalten kann. Statt dutzender einzelner PHP-Dateien im Projekt-Root entsteht ein einziger Einstiegspunkt, typischerweise bin/console, über den alle Befehle mit einheitlicher Hilfe, einheitlichem Exit-Code-Verhalten und einheitlicher Fehlerausgabe erreichbar sind.
<?php
declare(strict_types=1);
namespace App\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
// A minimal, self-contained command class
#[AsCommand(name: 'app:import', description: 'Import products from a CSV file')]
final class ImportCommand extends Command
{
protected function configure(): void
{
$this->setHelp('This command imports products from a given CSV file into the catalog.');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$output->writeln('Starting product import...');
// Business logic goes here
$output->writeln('Import finished.');
return Command::SUCCESS;
}
}
<?php
declare(strict_types=1);
// bin/console: single entry point for all CLI commands
require __DIR__ . '/../vendor/autoload.php';
use App\Command\ImportCommand;
use Symfony\Component\Console\Application;
$application = new Application('My CLI Tool', '1.0.0');
$application->add(new ImportCommand());
$application->run();
3. Argumente und Optionen definieren
Die Stärke von Symfony Console liegt in der klaren Trennung zwischen Argumenten und Optionen. Ein InputArgument ist ein positionsbasierter Wert, etwa der Pfad zu einer Datei, der ohne Präfix übergeben wird. Eine InputOption hingegen wird mit --name oder einem Kurzflag wie -n angegeben und ist standardmäßig optional. Beide lassen sich als verpflichtend (InputArgument::REQUIRED), optional (InputArgument::OPTIONAL) oder als Array (InputArgument::IS_ARRAY) deklarieren, wenn ein Argument mehrfach übergeben werden soll.
Optionen kennen zusätzlich den Modus InputOption::VALUE_NONE für reine Flags ohne Wert, etwa --dry-run, sowie InputOption::VALUE_REQUIRED und InputOption::VALUE_OPTIONAL für Optionen mit Wert. Auch bei Optionen ist ein Array-Modus möglich, sodass ein Flag wie --tag=foo --tag=bar mehrfach angegeben und als Liste eingesammelt werden kann. Diese Kombinatorik deckt praktisch jeden CLI-Anwendungsfall ab, ohne dass eigene Parser-Logik geschrieben werden muss.
Innerhalb von execute() werden die Werte typsicher über $input->getArgument() und $input->getOption() abgerufen. Fehlt ein als REQUIRED markiertes Argument beim Aufruf, bricht Symfony Console bereits vor dem Betreten von execute() mit einer verständlichen Fehlermeldung und dem passenden Exit-Code ab, ganz ohne zusätzlichen Code im Command selbst.
<?php
declare(strict_types=1);
namespace App\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand(name: 'app:import', description: 'Import products from one or more CSV files')]
final class ImportCommand extends Command
{
protected function configure(): void
{
$this
// Required positional argument
->addArgument('source', InputArgument::REQUIRED, 'Path to the CSV file')
// Optional argument with a default value
->addArgument('store', InputArgument::OPTIONAL, 'Target store code', 'default')
// Array argument: zero or more values
->addArgument('tags', InputArgument::IS_ARRAY, 'Tags to attach to imported products')
// Value-less flag
->addOption('dry-run', null, InputOption::VALUE_NONE, 'Simulate the import without writing data')
// Option with a required value and default
->addOption('batch-size', 'b', InputOption::VALUE_REQUIRED, 'Number of rows per batch', '100')
// Repeatable option collected into an array
->addOption('skip-column', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Columns to skip');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$source = $input->getArgument('source');
$batchSize = (int) $input->getOption('batch-size');
$isDryRun = (bool) $input->getOption('dry-run');
$output->writeln(sprintf('Importing %s in batches of %d (dry-run: %s)', $source, $batchSize, $isDryRun ? 'yes' : 'no'));
return Command::SUCCESS;
}
}
4. Interaktive Prompts und Validierung mit SymfonyStyle
Nicht jeder Parameter lässt sich sinnvoll über Argumente und Optionen vorwegnehmen, besonders bei destruktiven Operationen möchte man eine explizite Bestätigung im Terminal einholen. Symfony Console bietet dafür die Klasse SymfonyStyle, die einen einheitlichen visuellen Stil für alle Ausgaben liefert und gleichzeitig Zugriff auf die wichtigsten Prompt-Typen bereitstellt: ask() für Freitext, confirm() für Ja-Nein-Entscheidungen und choice() für eine Auswahl aus vordefinierten Optionen.
Besonders wertvoll ist die eingebaute Validierung. Der ask()-Methode kann eine Callback-Funktion übergeben werden, die den eingegebenen Wert prüft und bei ungültiger Eingabe eine Exception wirft. Symfony Console fängt diese Exception ab, zeigt die Fehlermeldung an und fragt automatisch erneut, ohne dass der aufrufende Code eine eigene Schleife schreiben muss. Das reduziert die typische Boilerplate-Logik für Eingabeprüfung auf eine einzige Zeile pro Regel.
Für sensible Eingaben wie Passwörter unterstützt ask() zusätzlich einen versteckten Eingabemodus, bei dem die Tastatureingabe im Terminal nicht sichtbar ist. In Kombination mit confirm() für kritische Aktionen, etwa das Löschen von Datensätzen, entsteht so ein CLI-Skript, das sich in puncto Bedienkomfort kaum von einer interaktiven Anwendung unterscheidet, ohne dass eine grafische Oberfläche nötig wäre.
<?php
declare(strict_types=1);
namespace App\Command;
use InvalidArgumentException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
final class SetupCommand extends Command
{
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
// Free text prompt with a validation callback
$email = $io->ask('Admin email address', null, function (?string $value): string {
if ($value === null || !str_contains($value, '@')) {
throw new InvalidArgumentException('Please provide a valid email address.');
}
return $value;
});
// Selection prompt with a predefined set of options
$environment = $io->choice('Target environment', ['development', 'staging', 'production'], 'development');
// Confirmation before a destructive action
if ($environment === 'production' && !$io->confirm('This will run against production. Continue?', false)) {
$io->warning('Aborted by user.');
return Command::FAILURE;
}
$io->success(sprintf('Configured admin "%s" for "%s".', $email, $environment));
return Command::SUCCESS;
}
}
5. Fortschrittsanzeigen und Tabellen
Sobald ein CLI-Skript größere Datenmengen verarbeitet, ob Produktimporte, Bildkonvertierungen oder Cache-Aufwärmvorgänge, wird eine Fortschrittsanzeige zum entscheidenden Feedback-Kanal für den Betreiber. Symfony Console bringt dafür die Klasse ProgressBar mit, die mit einer bekannten Gesamtanzahl an Schritten initialisiert und dann in einer Schleife mit advance() weitergeschaltet wird. Die Bar zeigt neben dem Fortschrittsbalken auch verstrichene Zeit und geschätzte Restdauer an, ganz ohne dass eigene Berechnungslogik nötig wäre.
Für tabellarische Ausgaben, etwa eine Übersicht verarbeiteter Datensätze oder eine Zusammenfassung am Ende eines Laufs, steht der Table-Helper zur Verfügung. Er übernimmt Spaltenbreiten, Rahmen und Zeilenumbrüche automatisch und erzeugt so lesbare Ausgaben, die sich an die tatsächliche Terminalbreite anpassen. Beide Helper lassen sich problemlos kombinieren: Während des Verarbeitens läuft die ProgressBar, am Ende fasst eine Table das Ergebnis zusammen.
Ein wichtiges Detail bei der Verwendung von ProgressBar: Sie schreibt direkt auf den Output-Stream und sollte deshalb nicht mit anderen writeln()-Aufrufen während des Fortschritts vermischt werden, da das die Anzeige zerreißen kann. Die übliche Praxis ist, Zwischenmeldungen erst nach finish() auszugeben, oder setMessage() zu verwenden, um zusätzlichen Text direkt in die Fortschrittszeile einzubetten.
6. Exit-Codes und robuste Fehlerbehandlung in CLI-Skripten
Ein oft übersehener, aber für Automatisierung entscheidender Aspekt von CLI-Skripten ist der Exit-Code. Cron-Jobs, CI-Pipelines und Monitoring-Systeme werten üblicherweise ausschließlich den numerischen Rückgabewert eines Prozesses aus, nicht seinen Ausgabetext. Symfony Console definiert dafür in der Command-Klasse drei benannte Konstanten: Command::SUCCESS (0) für einen erfolgreichen Lauf, Command::FAILURE (1) für einen fehlgeschlagenen Lauf und Command::INVALID (2) für ungültige Eingaben oder Aufrufparameter.
Diese Konstanten statt roher Zahlen zu verwenden, macht Commands selbstdokumentierend und verhindert Verwechslungen zwischen verschiedenen Fehlerarten. Ein Skript, das bei fehlender Berechtigung Command::FAILURE zurückgibt, bei einem falschen Parameter aber Command::INVALID, ermöglicht es nachgelagerten Systemen, gezielt zu unterscheiden, ob ein Retry sinnvoll ist oder ob eine Konfigurationsänderung nötig wäre.
Für unerwartete Fehler, etwa eine nicht erreichbare Datenbank oder eine defekte Eingabedatei, sollte execute() Exceptions gezielt abfangen und in einen passenden Exit-Code sowie eine verständliche Fehlermeldung über $io->error() übersetzen, statt die Exception unbehandelt bis zum globalen Handler durchzureichen. Symfony Console fängt zwar auch unbehandelte Exceptions ab und gibt einen Stacktrace sowie Exit-Code 1 aus, doch eine gezielte Behandlung liefert dem Betreiber eine deutlich verständlichere Fehlermeldung als einen rohen Stacktrace.
7. Commands testen mit CommandTester
Die konsequente Trennung von Konfiguration und Ausführung in Command-Klassen zahlt sich bei der Testbarkeit direkt aus. Die Klasse CommandTester aus dem Paket symfony/console erlaubt es, einen Command ohne echten Terminal-Aufruf und ohne laufenden Prozess auszuführen. Sie simuliert Eingaben, fängt die Ausgabe ab und stellt Assertions über Exit-Code und Ausgabetext bereit, sodass CLI-Logik genauso zuverlässig getestet werden kann wie jede andere PHP-Klasse.
Für Commands mit interaktiven Prompts bietet CommandTester::setInputs() die Möglichkeit, eine Liste simulierter Terminaleingaben vorzugeben, die dann nacheinander an ask(), confirm() oder choice() geliefert werden. Dadurch lassen sich auch komplexe interaktive Abläufe deterministisch testen, ohne dass ein echter Anwender am Terminal sitzen muss. Die Methode getDisplay() liefert die vollständige Ausgabe als String zurück, sodass sich Erfolgsmeldungen, Fehlertexte oder Tabellenzeilen gezielt prüfen lassen.
In einer CI-Pipeline bedeutet das: CLI-Skripte werden nicht länger nur manuell im Terminal ausprobiert, sondern laufen bei jedem Commit automatisiert durch PHPUnit. Regressionen, etwa ein versehentlich vertauschtes Argument oder ein falsch berechneter Exit-Code, fallen so auf, bevor sie in Produktion ausgeführt werden.
<?php
declare(strict_types=1);
namespace App\Tests\Command;
use App\Command\ImportCommand;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Tester\CommandTester;
final class ImportCommandTest extends TestCase
{
public function testImportSucceedsWithValidSource(): void
{
$application = new Application();
$application->add(new ImportCommand());
$command = $application->find('app:import');
$tester = new CommandTester($command);
// Simulate answers for any interactive prompts
$tester->setInputs(['yes']);
$exitCode = $tester->execute([
'source' => 'fixtures/products.csv',
'--dry-run' => true,
]);
self::assertSame(Command::SUCCESS, $exitCode);
self::assertStringContainsString('Import finished.', $tester->getDisplay());
}
public function testImportFailsWithMissingRequiredArgument(): void
{
$application = new Application();
$application->add(new ImportCommand());
$tester = new CommandTester($application->find('app:import'));
// Missing the required "source" argument raises a RuntimeException
$this->expectException(\RuntimeException::class);
$tester->execute([]);
}
}
8. Scheduling und Locking: die Symfony Lock-Component und Cron-Integration
CLI-Skripte, die per Cron oder als Kubernetes-CronJob geplant laufen, bergen ein spezifisches Risiko: Läuft ein Skript ungewöhnlich lange, etwa weil ein externer API-Aufruf hängt, startet der nächste Cron-Trigger den Command möglicherweise erneut, während die vorherige Ausführung noch aktiv ist. Zwei parallele Läufe desselben Import- oder Synchronisations-Commands können zu doppelten Datensätzen, Race Conditions oder inkonsistentem Zustand führen.
Die Symfony Lock-Component, ein weiteres eigenständiges Composer-Paket, löst genau dieses Problem mit einer LockFactory. Ein Command fordert zu Beginn ein Lock für einen eindeutigen Ressourcennamen an. Gelingt der Erwerb nicht, weil bereits ein anderer Prozess dasselbe Lock hält, beendet sich der Command sofort mit einem klaren Hinweis statt parallel weiterzulaufen. Das Lock wird über einen konfigurierbaren Store gehalten, etwa eine Datei, Redis oder eine Datenbanktabelle, wodurch die Lösung auch über mehrere Server hinweg funktioniert.
In der Praxis kombiniert man den Cron-Eintrag mit einer definierten Lock-Strategie im Command selbst. Der Cron-Daemon kümmert sich nur um die zeitliche Auslösung, während Symfony Console zusammen mit der Lock-Component sicherstellt, dass zu jedem Zeitpunkt höchstens eine Instanz des Commands aktiv arbeitet, unabhängig davon, wie oft der Cron-Trigger in der Zwischenzeit ausgelöst wurde.
# crontab entry: run every 5 minutes, output goes to a log file
*/5 * * * * /usr/bin/php /var/www/bin/console app:sync-orders >> /var/log/sync-orders.log 2>&1
<?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;
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\Store\FlockStore;
final class SyncOrdersCommand extends Command
{
private LockFactory $lockFactory;
public function __construct()
{
parent::__construct();
// File-based store keeps this example dependency-free
$this->lockFactory = new LockFactory(new FlockStore());
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$lock = $this->lockFactory->createLock('app:sync-orders');
// Non-blocking: fail fast if another instance already holds the lock
if (!$lock->acquire()) {
$output->writeln('Another instance is already running, skipping this run.');
return Command::SUCCESS;
}
try {
$output->writeln('Syncing orders...');
// Synchronization logic goes here
} finally {
$lock->release();
}
return Command::SUCCESS;
}
}
9. Von Skript zu Tool: Packaging als eigenständige PHAR-Datei
Sobald ein CLI-Tool über den eigenen Projektrahmen hinaus verteilt werden soll, etwa als internes Werkzeug für mehrere Teams oder als öffentlich verfügbares Kommandozeilenprogramm, ist eine PHAR-Datei die gängige Verteilungsform. Ein PHAR bündelt alle PHP-Quelldateien, die Composer-Abhängigkeiten und einen Bootstrap-Einstiegspunkt in einer einzigen ausführbaren Datei, die ohne separate vendor-Installation auf jedem System mit passender PHP-Version läuft.
Das Tool box (humbug/box) hat sich als De-facto-Standard für das Bauen von PHARs aus Symfony Console-Anwendungen etabliert. Es liest eine box.json-Konfigurationsdatei, in der Einstiegspunkt, einzuschließende Verzeichnisse und Optimierungen wie Kompression definiert werden, und erzeugt daraus eine einzelne, versionierte PHAR-Datei. Alternativ lässt sich ein PHAR auch direkt mit der eingebauten Phar-Klasse von PHP erzeugen, box übernimmt jedoch typische Fallstricke wie das korrekte Einbinden des Composer-Autoloaders automatisch.
Ein wichtiger Hinweis für Produktionsumgebungen: PHAR-Ausführung erfordert, dass die PHP-Ini-Einstellung phar.readonly nicht das Erstellen neuer PHARs blockiert, während bereits gebaute PHARs in aller Regel unabhängig von dieser Einstellung ausgeführt werden können. Nach dem Bau lässt sich die PHAR-Datei mit chmod +x direkt ausführbar machen und über den PATH systemweit verfügbar machen, wodurch aus dem ursprünglichen CLI-Skript ein vollwertiges, verteilbares Kommandozeilenwerkzeug wird.
# Install box as a dev dependency or as a global tool
composer require --dev humbug/box
# box.json defines entry point, directories and compression
# {
# "main": "bin/console",
# "output": "build/tool.phar",
# "directories": ["src", "vendor"],
# "compression": "GZ"
# }
# Build the PHAR
php vendor/bin/box compile
# Make it executable and run it directly
chmod +x build/tool.phar
./build/tool.phar app:import fixtures/products.csv
10. Zusammenfassung
Die Symfony-Console-Komponente verwandelt rohe $argv-Skripte in strukturierte, getestete CLI-Werkzeuge, ohne dass das komplette Symfony-Framework installiert werden muss. Command-Klassen trennen Deklaration und Ausführung sauber, InputArgument und InputOption übernehmen Validierung und Hilfetext automatisch, und SymfonyStyle liefert einheitliche, interaktive Prompts mit eingebauter Validierung. Fortschrittsanzeigen und Tabellen machen auch langlaufende Batch-Jobs für Betreiber nachvollziehbar.
Für den produktiven Einsatz sind drei Aspekte entscheidend: klare Exit-Codes über Command::SUCCESS, FAILURE und INVALID für Monitoring und Automatisierung, CommandTester für automatisierte Tests in der CI-Pipeline, und die Symfony Lock-Component, um parallele Ausführungen desselben geplanten Commands zu verhindern. Wer diese Bausteine konsequent nutzt und das fertige Tool bei Bedarf als PHAR packagt, erhält aus einem einfachen CLI-Skript ein robustes, verteilbares Werkzeug für den gesamten Entwicklungs- und Betriebsalltag.
Robuste CLI-Skripte mit Symfony Console - Das Wichtigste auf einen Blick
Command-Struktur
Eine Command-Klasse pro Befehl, configure() für Deklaration, execute() für Logik. Registriert über eine zentrale Application.
Argumente & Prompts
InputArgument und InputOption validieren automatisch. SymfonyStyle liefert Prompts mit Validierungs-Callback.
Tests & Exit-Codes
CommandTester prüft Ausgabe und Status ohne echten Terminal-Aufruf. Command::SUCCESS/FAILURE/INVALID für klare Automatisierung.
Locking & Packaging
Symfony Lock verhindert parallele Cron-Läufe. box baut das fertige Tool zu einer einzigen, verteilbaren PHAR-Datei.
11. FAQ: Robuste CLI-Skripte mit Symfony Console
1Was ist die Symfony-Console-Komponente?
2Braucht man das ganze Symfony-Framework dafür?
3InputArgument vs. InputOption?
4Wie erzwinge ich ein Pflichtargument?
5Was macht SymfonyStyle?
6Wie teste ich einen Command ohne Terminal?
7Welche Exit-Codes gibt es?
8Parallele Cron-Läufe verhindern?
9Fortschritt bei langen Skripten anzeigen?
10Was ist eine PHAR-Datei?
Mironsoft
PHP-Entwicklung, CLI-Automatisierung und Magento-Expertise
CLI-Skripte, die im Alltag wirklich tragen?
Wir bauen wartbare Automatisierungs-Tools auf Basis von Symfony Console, mit vollständiger Testabdeckung, sauberen Exit-Codes und Locking-Strategien für den produktiven Dauerbetrieb.
Command-Design
Saubere Command-Klassen mit InputArgument, InputOption und aussagekräftigen Exit-Codes
Test-Automatisierung
CommandTester-Suiten in der CI-Pipeline für zuverlässige CLI-Logik
Scheduling & Packaging
Symfony Lock gegen parallele Läufe, PHAR-Builds für verteilbare Tools