mirror of
https://github.com/symfony/symfony-docs.git
synced 2026-03-24 00:32:14 +01:00
540 lines
18 KiB
ReStructuredText
540 lines
18 KiB
ReStructuredText
How to Style a Console Command
|
|
==============================
|
|
|
|
One of the most boring tasks when creating console commands is to deal with the
|
|
styling of the command's input and output. Displaying titles and tables or asking
|
|
questions to the user involves a lot of repetitive code.
|
|
|
|
Consider for example the code used to display the title of the following command::
|
|
|
|
// src/Command/MyCommand.php
|
|
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;
|
|
|
|
#[AsCommand(name: 'app:my-command')]
|
|
class MyCommand
|
|
{
|
|
public function __invoke(InputInterface $input, OutputInterface $output): int
|
|
{
|
|
$output->writeln([
|
|
'<info>Lorem Ipsum Dolor Sit Amet</>',
|
|
'<info>==========================</>',
|
|
'',
|
|
]);
|
|
|
|
// ...
|
|
}
|
|
}
|
|
|
|
Displaying a simple title requires three lines of code, to change the font color,
|
|
underline the contents and leave an additional blank line after the title. Dealing
|
|
with styles is required for well-designed commands, but it complicates their code
|
|
unnecessarily.
|
|
|
|
In order to reduce that boilerplate code, Symfony commands can optionally use the
|
|
**Symfony Style Guide**. These styles are implemented as a set of helper methods
|
|
which allow you to create *semantic* commands and forget about their styling.
|
|
|
|
Basic Usage
|
|
-----------
|
|
|
|
In your ``__invoke()`` method, add an argument of type :class:`Symfony\\Component\\Console\\Style\\SymfonyStyle`.
|
|
Then, you can start using any of its helpers, such as ``title()``, which
|
|
displays the title of the command::
|
|
|
|
// src/Command/MyCommand.php
|
|
namespace App\Command;
|
|
|
|
use Symfony\Component\Console\Attribute\AsCommand;
|
|
use Symfony\Component\Console\Command\Command;
|
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
|
|
|
#[AsCommand(name: 'app:my-command')]
|
|
class MyCommand
|
|
{
|
|
public function __invoke(SymfonyStyle $io): int
|
|
{
|
|
$io->title('Lorem Ipsum Dolor Sit Amet');
|
|
|
|
// ...
|
|
}
|
|
}
|
|
|
|
Helper Methods
|
|
--------------
|
|
|
|
The :class:`Symfony\\Component\\Console\\Style\\SymfonyStyle` class defines some
|
|
helper methods that cover the most common interactions performed by console commands.
|
|
|
|
Titling Methods
|
|
~~~~~~~~~~~~~~~
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::title`
|
|
It displays the given string as the command title. This method is meant to
|
|
be used only once in a given command, but nothing prevents you to use it
|
|
repeatedly::
|
|
|
|
$io->title('Lorem ipsum dolor sit amet');
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::section`
|
|
It displays the given string as the title of some command section. This is
|
|
only needed in complex commands which want to better separate their contents::
|
|
|
|
$io->section('Adding a User');
|
|
|
|
// ...
|
|
|
|
$io->section('Generating the Password');
|
|
|
|
// ...
|
|
|
|
.. _symfony-style-content:
|
|
|
|
Content Methods
|
|
~~~~~~~~~~~~~~~
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::text`
|
|
It displays the given string or array of strings as regular text. This is
|
|
useful to render help messages and instructions for the user running the
|
|
command::
|
|
|
|
// use simple strings for short messages
|
|
$io->text('Lorem ipsum dolor sit amet');
|
|
|
|
// ...
|
|
|
|
// consider using arrays when displaying long messages
|
|
$io->text([
|
|
'Lorem ipsum dolor sit amet',
|
|
'Consectetur adipiscing elit',
|
|
'Aenean sit amet arcu vitae sem faucibus porta',
|
|
]);
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::listing`
|
|
It displays an unordered list of elements passed as an array::
|
|
|
|
$io->listing([
|
|
'Element #1 Lorem ipsum dolor sit amet',
|
|
'Element #2 Lorem ipsum dolor sit amet',
|
|
'Element #3 Lorem ipsum dolor sit amet',
|
|
]);
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::table`
|
|
It displays the given array of headers and rows as a compact table::
|
|
|
|
$io->table(
|
|
['Header 1', 'Header 2'],
|
|
[
|
|
['Cell 1-1', 'Cell 1-2'],
|
|
['Cell 2-1', 'Cell 2-2'],
|
|
['Cell 3-1', 'Cell 3-2'],
|
|
]
|
|
);
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::horizontalTable`
|
|
It displays the given array of headers and rows as a compact horizontal table::
|
|
|
|
$io->horizontalTable(
|
|
['Header 1', 'Header 2'],
|
|
[
|
|
['Cell 1-1', 'Cell 1-2'],
|
|
['Cell 2-1', 'Cell 2-2'],
|
|
['Cell 3-1', 'Cell 3-2'],
|
|
]
|
|
);
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::definitionList`
|
|
It displays the given ``key => value`` pairs as a compact list of elements::
|
|
|
|
$io->definitionList(
|
|
'This is a title',
|
|
['foo1' => 'bar1'],
|
|
['foo2' => 'bar2'],
|
|
['foo3' => 'bar3'],
|
|
new TableSeparator(),
|
|
'This is another title',
|
|
['foo4' => 'bar4']
|
|
);
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::createTable`
|
|
Creates an instance of :class:`Symfony\\Component\\Console\\Helper\\Table`
|
|
styled according to the Symfony Style Guide, which allows you to use
|
|
features such as dynamically appending rows.
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::tree`
|
|
It displays the given nested array as a formatted directory/file tree
|
|
structure in the console output::
|
|
|
|
$io->tree([
|
|
'src' => [
|
|
'Controller' => [
|
|
'DefaultController.php',
|
|
],
|
|
'Kernel.php',
|
|
],
|
|
'templates' => [
|
|
'base.html.twig',
|
|
],
|
|
]);
|
|
|
|
.. versionadded:: 7.3
|
|
|
|
The ``SymfonyStyle::tree()`` and the ``SymfonyStyle::createTree()`` methods
|
|
were introduced in Symfony 7.3.
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::createTree`
|
|
Creates an instance of :class:`Symfony\\Component\\Console\\Helper\\TreeHelper`
|
|
styled according to the Symfony Style Guide, which allows you to use
|
|
features such as dynamically nesting nodes.
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::newLine`
|
|
It displays a blank line in the command output. Although it may seem useful,
|
|
most of the times you won't need it at all. The reason is that every helper
|
|
already adds their own blank lines, so you don't have to care about the
|
|
vertical spacing::
|
|
|
|
// outputs a single blank line
|
|
$io->newLine();
|
|
|
|
// outputs three consecutive blank lines
|
|
$io->newLine(3);
|
|
|
|
Admonition Methods
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::note`
|
|
It displays the given string or array of strings as a highlighted admonition.
|
|
Use this helper sparingly to avoid cluttering command's output::
|
|
|
|
// use simple strings for short notes
|
|
$io->note('Lorem ipsum dolor sit amet');
|
|
|
|
// ...
|
|
|
|
// consider using arrays when displaying long notes
|
|
$io->note([
|
|
'Lorem ipsum dolor sit amet',
|
|
'Consectetur adipiscing elit',
|
|
'Aenean sit amet arcu vitae sem faucibus porta',
|
|
]);
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::caution`
|
|
Similar to the ``note()`` helper, but the contents are more prominently
|
|
highlighted. The resulting contents resemble an error message, so you should
|
|
avoid using this helper unless strictly necessary::
|
|
|
|
// use simple strings for short caution message
|
|
$io->caution('Lorem ipsum dolor sit amet');
|
|
|
|
// ...
|
|
|
|
// consider using arrays when displaying long caution messages
|
|
$io->caution([
|
|
'Lorem ipsum dolor sit amet',
|
|
'Consectetur adipiscing elit',
|
|
'Aenean sit amet arcu vitae sem faucibus porta',
|
|
]);
|
|
|
|
.. _symfony-style-progressbar:
|
|
|
|
Progress Bar Methods
|
|
~~~~~~~~~~~~~~~~~~~~
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::progressStart`
|
|
It displays a progress bar with a number of steps equal to the argument passed
|
|
to the method (don't pass any value if the length of the progress bar is
|
|
unknown)::
|
|
|
|
// displays a progress bar of unknown length
|
|
$io->progressStart();
|
|
|
|
// displays a 100-step length progress bar
|
|
$io->progressStart(100);
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::progressAdvance`
|
|
It makes the progress bar advance the given number of steps (or ``1`` step
|
|
if no argument is passed)::
|
|
|
|
// advances the progress bar 1 step
|
|
$io->progressAdvance();
|
|
|
|
// advances the progress bar 10 steps
|
|
$io->progressAdvance(10);
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::progressFinish`
|
|
It finishes the progress bar (filling up all the remaining steps when its
|
|
length is known)::
|
|
|
|
$io->progressFinish();
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::progressIterate`
|
|
If your progress bar loops over an iterable collection, use the
|
|
``progressIterate()`` helper::
|
|
|
|
$iterable = [1, 2];
|
|
|
|
foreach ($io->progressIterate($iterable) as $value) {
|
|
// ... do some work
|
|
}
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::createProgressBar`
|
|
Creates an instance of :class:`Symfony\\Component\\Console\\Helper\\ProgressBar`
|
|
styled according to the Symfony Style Guide.
|
|
|
|
.. _symfony-style-questions:
|
|
|
|
User Input Methods
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::ask`
|
|
It asks the user to provide some value::
|
|
|
|
$io->ask('What is your name?');
|
|
|
|
You can pass the default value as the second argument so the user can
|
|
hit the <Enter> key to select that value::
|
|
|
|
$io->ask('Where are you from?', 'United States');
|
|
|
|
In case you need to validate the given value, pass a callback validator as
|
|
the third argument::
|
|
|
|
$io->ask('Number of workers to start', '1', function (string $number): int {
|
|
if (!is_numeric($number)) {
|
|
throw new \RuntimeException('You must type a number.');
|
|
}
|
|
|
|
return (int) $number;
|
|
});
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::askHidden`
|
|
It's very similar to the ``ask()`` method but the user's input will be hidden
|
|
and it cannot define a default value. Use it when asking for sensitive information::
|
|
|
|
$io->askHidden('What is your password?');
|
|
|
|
In case you need to validate the given value, pass a callback validator as
|
|
the second argument::
|
|
|
|
$io->askHidden('What is your password?', function (string $password): string {
|
|
if (empty($password)) {
|
|
throw new \RuntimeException('Password cannot be empty.');
|
|
}
|
|
|
|
return $password;
|
|
});
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::confirm`
|
|
It asks a Yes/No question to the user and it only returns ``true`` or ``false``::
|
|
|
|
$io->confirm('Restart the web server?');
|
|
|
|
You can pass the default value as the second argument so the user can
|
|
hit the <Enter> key to select that value::
|
|
|
|
$io->confirm('Restart the web server?', true);
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::choice`
|
|
It asks a question whose answer is constrained to the given list of valid
|
|
answers::
|
|
|
|
$io->choice('Select the queue to analyze', ['queue1', 'queue2', 'queue3']);
|
|
|
|
You can pass the default value as the third argument so the user can
|
|
hit the <Enter> key to select that value::
|
|
|
|
$io->choice('Select the queue to analyze', ['queue1', 'queue2', 'queue3'], 'queue1');
|
|
|
|
Choice questions display both the choice value and a numeric index, which
|
|
starts from ``0`` by default. To use custom indices, pass an array with
|
|
custom numeric keys as the choice values::
|
|
|
|
$io->choice('Select the queue to analyze', [5 => 'queue1', 6 => 'queue2', 7 => 'queue3']);
|
|
|
|
Finally, you can allow users to select multiple choices. To do so, users must
|
|
separate each choice with a comma (e.g. typing ``1, 2`` will select choice 1
|
|
and 2)::
|
|
|
|
$io->choice('Select the queue to analyze', ['queue1', 'queue2', 'queue3'], multiSelect: true);
|
|
|
|
.. _symfony-style-blocks:
|
|
|
|
Result Methods
|
|
~~~~~~~~~~~~~~
|
|
|
|
.. note::
|
|
|
|
If you print any URL it won't be broken/cut, it will be clickable - if the terminal provides it. If the "well
|
|
formatted output" is more important, you can switch it off::
|
|
|
|
$io->getOutputWrapper()->setAllowCutUrls(true);
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::success`
|
|
It displays the given string or array of strings highlighted as a successful
|
|
message (with a green background and the ``[OK]`` label). It's meant to be
|
|
used once to display the final result of executing the given command, but you
|
|
can use it repeatedly during the execution of the command::
|
|
|
|
// use simple strings for short success messages
|
|
$io->success('Lorem ipsum dolor sit amet');
|
|
|
|
// ...
|
|
|
|
// consider using arrays when displaying long success messages
|
|
$io->success([
|
|
'Lorem ipsum dolor sit amet',
|
|
'Consectetur adipiscing elit',
|
|
]);
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::info`
|
|
It's similar to the ``success()`` method (the given string or array of strings
|
|
are displayed with a green background) but the ``[OK]`` label is not prefixed.
|
|
It's meant to be used once to display the final result of executing the given
|
|
command, without showing the result as a successful or failed one::
|
|
|
|
// use simple strings for short info messages
|
|
$io->info('Lorem ipsum dolor sit amet');
|
|
|
|
// ...
|
|
|
|
// consider using arrays when displaying long info messages
|
|
$io->info([
|
|
'Lorem ipsum dolor sit amet',
|
|
'Consectetur adipiscing elit',
|
|
]);
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::warning`
|
|
It displays the given string or array of strings highlighted as a warning
|
|
message (with a red background and the ``[WARNING]`` label). It's meant to be
|
|
used once to display the final result of executing the given command, but you
|
|
can use it repeatedly during the execution of the command::
|
|
|
|
// use simple strings for short warning messages
|
|
$io->warning('Lorem ipsum dolor sit amet');
|
|
|
|
// ...
|
|
|
|
// consider using arrays when displaying long warning messages
|
|
$io->warning([
|
|
'Lorem ipsum dolor sit amet',
|
|
'Consectetur adipiscing elit',
|
|
]);
|
|
|
|
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::error`
|
|
It displays the given string or array of strings highlighted as an error
|
|
message (with a red background and the ``[ERROR]`` label). It's meant to be
|
|
used once to display the final result of executing the given command, but you
|
|
can use it repeatedly during the execution of the command::
|
|
|
|
// use simple strings for short error messages
|
|
$io->error('Lorem ipsum dolor sit amet');
|
|
|
|
// ...
|
|
|
|
// consider using arrays when displaying long error messages
|
|
$io->error([
|
|
'Lorem ipsum dolor sit amet',
|
|
'Consectetur adipiscing elit',
|
|
]);
|
|
|
|
Configuring the Default Styles
|
|
------------------------------
|
|
|
|
By default, Symfony Styles wrap all contents to avoid having lines of text that
|
|
are too long. The only exception is URLs, which are not wrapped, no matter how
|
|
long they are. This is done to enable clickable URLs in terminals that support them.
|
|
|
|
If you prefer to wrap all contents, including URLs, use this method::
|
|
|
|
// src/Command/MyCommand.php
|
|
namespace App\Command;
|
|
|
|
// ...
|
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
|
|
|
#[AsCommand(name: 'app:my-command')]
|
|
class MyCommand
|
|
{
|
|
public function __invoke(SymfonyStyle $io): int
|
|
{
|
|
$io->getOutputWrapper()->setAllowCutUrls(true);
|
|
|
|
// ...
|
|
}
|
|
}
|
|
|
|
Defining your Own Styles
|
|
------------------------
|
|
|
|
If you don't like the design of the commands that use the Symfony Style, you can
|
|
define your own set of console styles. Create a class that implements the
|
|
:class:`Symfony\\Component\\Console\\Style\\StyleInterface`::
|
|
|
|
namespace App\Console;
|
|
|
|
use Symfony\Component\Console\Style\StyleInterface;
|
|
|
|
class CustomStyle implements StyleInterface
|
|
{
|
|
// ...implement the methods of the interface
|
|
}
|
|
|
|
Then, instantiate this custom class instead of the default ``SymfonyStyle`` in
|
|
your commands. Thanks to the ``StyleInterface`` you won't need to change the code
|
|
of your commands to change their appearance::
|
|
|
|
// src/Command/MyCommand.php
|
|
namespace App\Command;
|
|
|
|
use App\Console\CustomStyle;
|
|
use Symfony\Component\Console\Command\Command;
|
|
use Symfony\Component\Console\Input\InputInterface;
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
|
|
|
#[AsCommand(name: 'app:my-command')]
|
|
class MyCommand
|
|
{
|
|
public function __invoke(InputInterface $input, OutputInterface $output): int
|
|
{
|
|
$io = new CustomStyle($input, $output);
|
|
|
|
// ...
|
|
}
|
|
}
|
|
|
|
Writing to the error output
|
|
---------------------------
|
|
|
|
If you reuse the output of a command as the input of other commands or dump it
|
|
into a file for later reuse, you probably want to exclude progress bars, notes
|
|
and other output that provides no real value.
|
|
|
|
Commands can output information in two different streams: ``stdout`` (standard
|
|
output) is the stream where the real contents should be output and ``stderr``
|
|
(standard error) is the stream where the errors and the debugging messages
|
|
should be output.
|
|
|
|
The :class:`Symfony\\Component\\Console\\Style\\SymfonyStyle` class provides a
|
|
convenient method called :method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::getErrorStyle`
|
|
to switch between both streams. This method returns a new ``SymfonyStyle``
|
|
instance which makes use of the error output::
|
|
|
|
$io = new SymfonyStyle($input, $output);
|
|
|
|
// Write to the standard output
|
|
$io->write('Reusable information');
|
|
|
|
// Write to the error output
|
|
$io->getErrorStyle()->warning('Debugging information or errors');
|
|
|
|
.. note::
|
|
|
|
If you create a ``SymfonyStyle`` instance with an ``OutputInterface`` object
|
|
that is not an instance of :class:`Symfony\\Component\\Console\\Output\\ConsoleOutputInterface`,
|
|
the ``getErrorStyle()`` method will have no effect and the returned object
|
|
will still write to the standard output instead of the error output.
|