Symfony Console
Úvod
- knihovna pro konzolové aplikace - oproti ručnímu zpracování
$argvnebogetopt()řeší automaticky- nápovědu (
--help) a seznam příkazů - kontrolu povinných argumentů a převod hodnot na správný typ
- srozumitelné chybové hlášky
- nápovědu (
instalace
composer require symfony/console- dokumentace
- konzolová aplikace
Symfony\Component\Console\Application - jednotlivé příkazy lze psát dvěma způsoby
- invokable command - třída s metodou
__invoke(), argumenty a přepínače se definují pomocí atributů (od Symfony 7.3) - starší způsob - třída dědí od
Symfony\Component\Console\Command\Commanda implementujeconfigure()aexecute()
- invokable command - třída s metodou
Ukázka
$ php cli.php image:resize --help
Description:
Resize image
Usage:
image:resize [options] [--] <input>
Arguments:
input Input image file
Options:
-w, --width=WIDTH Output image width [default: 640]
...
$ php cli.php image:resize
Not enough arguments (missing: "input").
image:resize [-w|--width WIDTH] [--] <input>Aplikace
jednotlivé příkazy se zaregistrují pomocí
addCommand()// cli.php require 'vendor/autoload.php'; use Symfony\Component\Console\Application; $app = new Application(); $app->addCommand(new ResizeCommand()); // viz Invokable command // ... $app->run();- v plném Symfony frameworku se příkazy zaregistrují automaticky
- spuštění příkazu - název příkazu, za ním argumenty a přepínače
bez názvu příkazu se vypíše seznam všech příkazů (stejně jako
list)$ php cli.php image:resize picture.png $ php cli.php list
Invokable command
- atribut
AsCommanda metoda__invoke() - parametr
SymfonyStyle $ioslouží pro výstup, viz Výstup - SymfonyStyle argumenty a přepínače se definují jako parametry metody pomocí atributů
ArgumentaOptionuse Symfony\Component\Console\Attribute\Argument; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\Option; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Style\SymfonyStyle; #[AsCommand(name: 'image:resize', description: 'Resize image')] class ResizeCommand { public function __invoke( SymfonyStyle $io, #[Argument(description: 'Input image file')] string $input, #[Option(description: 'Output image width', shortcut: 'w')] int $width = 640, ): int { $io->writeln("Resizing $input to width $width"); return Command::SUCCESS; } }
Název, popis, nápověda
- nastavují se v atributu
AsCommandname- název příkazudescription- popis (zobrazí se ve výpisu příkazů)help- nápověda (--help)#[AsCommand( name: 'image:resize', description: 'Resize image', help: 'Resize PNG or JPEG and save result to new file', )]
Příkazy se často člení do skupin podle :, při volání stačí použít jednoznačnou zkratku
$ php cli.php i:r picture.pngArgumenty a přepínače
- argumenty a přepínače jsou parametry metody
__invoke()označené atributem#[Argument], resp.#[Option] - Symfony většinu informací zjistí z deklarace parametru
- název - z názvu parametru převedeného na kebab-case (
$maxWidth→--max-width), lze změnit pomocíname - povinnost - podle toho, zda má parametr výchozí hodnotu
- výchozí hodnota - z výchozí hodnoty parametru (zobrazí se i v
--help) - typ - z typehintu (proto je povinný)
- hodnota z příkazové řádky se na daný typ převede (
int,float,bool,array, backed enum) - neplatnou hodnotu Symfony odmítne s chybovou hláškou (např.
--max-width=abcproint)
- hodnota z příkazové řádky se na daný typ převede (
- název - z názvu parametru převedeného na kebab-case (
- v atributu se uvádí jen to, co z parametru zjistit nelze, např. popis (
description) - backed enum jako typ - omezí hodnotu na výčet
- přípustné hodnoty jsou hodnoty jednotlivých
case - do parametru se předá rovnou instance enumu (např.
Format::Jpeg) při neplatné hodnotě chybová hláška vypíše přípustné hodnoty
enum Format: string { case Png = 'png'; case Jpeg = 'jpeg'; } // --format=png nebo --format=jpeg, jiná hodnota skončí chybou #[Option(description: 'Output format')] Format $format = Format::Png,
- přípustné hodnoty jsou hodnoty jednotlivých
- Understanding how Console Arguments and Options Are Handled
Argumenty
- atribut
#[Argument](dokumentace) - bez výchozí hodnoty povinný, s výchozí hodnotou (nebo nullable) volitelný
- typ
array- více hodnot není nutné explicitně uvádět (ne)povinnost
// povinný argument #[Argument(description: 'Input image file')] string $input, // volitelný argument #[Argument(description: 'Output image file')] ?string $output = null,
Přepínače
- atribut
#[Option](dokumentace) - musí mít výchozí hodnotu
- typ
bool- přepínač bez hodnoty, s výchozí hodnotoutruei v negované podobě (--color/--no-color) - typ
array- více hodnot krátká varianta pomocí
shortcut// -w320, -w 320, --width 320, --width=320 #[Option(description: 'Output image width', shortcut: 'w')] int $width = 640, // --force #[Option(description: 'Overwrite output')] bool $force = false, // --color, --no-color #[Option(description: 'Use colors')] bool $color = true,
Provádění příkazu
- do
__invoke()lze kromě argumentů a přepínačů nechat předat i další objekty, rozhoduje typ parametru (pořadí parametrů nehraje roli)SymfonyStyle- pohodlný výstup (writeln(),success(),error(),table(), …)OutputInterface,InputInterface- přímý přístup ke vstupu a výstupu
- výstup viz SymfonyStyle
- návratová hodnota musí být
int(exit kód), např.Command::SUCCESS,Command::FAILURE,Command::INVALID
Výstup - SymfonyStyle
- jednotný vzhled výstupu všech příkazů (dokumentace)
text -
writeln(),title(),section(),listing(),table()$io->title('Image info'); $io->table(['File', 'Width', 'Height'], [ ['photo.png', 640, 480], ['logo.png', 128, 128], ]);Image info ========== ----------- ------- -------- File Width Height ----------- ------- -------- photo.png 640 480 logo.png 128 128 ----------- ------- --------- zprávy -
success(),error(),warning(),info() - progress bar -
progressIterate($iterable) interakce -
ask(),confirm(),choice()$format = $io->choice('Output format', ['png', 'jpeg'], 'png'); if (!$io->confirm("Convert $input to $format?")) { return Command::FAILURE; } $io->success("Saved to image.$format");$ php cli.php image:convert photo.png Output format [png]: [0] png [1] jpeg > jpeg Convert photo.png to jpeg? (yes/no) [yes]: > yes [OK] Saved to image.jpeg
Method commands
- od Symfony 8.1 lze atribut
AsCommandpoužít i na metodu - jedna třída tak může definovat více příkazů název ve třídním atributu slouží jako prefix (vznikne
image:resizeaimage:crop)#[AsCommand(name: 'image')] class ImageCommands { #[AsCommand(name: 'resize', description: 'Resize image')] public function resize( SymfonyStyle $io, #[Argument] string $input ): int { /* ... */ } #[AsCommand(name: 'crop', description: 'Crop image')] public function crop( SymfonyStyle $io, #[Argument] string $input ): int { /* ... */ } } $commands = new ImageCommands(); $app->addCommand($commands->resize(...)); $app->addCommand($commands->crop(...));
Starší způsob
- stále podporovaný a častý v existujícím kódu
- nutný pro Symfony starší než 7.3
Dědění od Command
nastavení v metodě
configure(), provádění v metoděexecute()#[AsCommand(name: 'image:resize', description: 'Resize image')] class ResizeCommand extends Command { protected function configure(): void { $this ->setHelp('Resize PNG or JPEG and save result to new file') ->addArgument('input', InputArgument::REQUIRED, 'Input image file') ->addOption('width', 'w', InputOption::VALUE_REQUIRED, 'Output image width', 640) ; } protected function execute(InputInterface $input, OutputInterface $output): int { $output->writeln('Resizing ' . $input->getArgument('input') . ' to width ' . $input->getOption('width')); return Command::SUCCESS; } }
Argumenty a přepínače
- argumenty -
addArgument(název, režim, popis, výchozí hodnota)InputArgument::REQUIRED,InputArgument::OPTIONAL- povinný, volitelnýInputArgument::IS_ARRAY- více hodnot, kombinuje se pomocí|(např.InputArgument::REQUIRED | InputArgument::IS_ARRAY)
- přepínače -
addOption(název, krátká varianta, režim, popis, výchozí hodnota)InputOption::VALUE_NONE- přepínač bez hodnoty (--force)InputOption::VALUE_REQUIRED,InputOption::VALUE_OPTIONAL- hodnota povinná, nepovinnáInputOption::VALUE_IS_ARRAY- více hodnot, kombinuje se pomocí|sVALUE_REQUIREDneboVALUE_OPTIONALInputOption::VALUE_NEGATABLE- přepínač lze zadat i v negované podobě (--color/--no-color)
- vstup
$input->getArgument('…')$input->getOption('…')
Výstup
$output->write('…')$output->writeln('…')SymfonyStylelze vytvořit i ručně -$io = new SymfonyStyle($input, $output), viz Výstup - SymfonyStyle