Skip to content

[Console] Document invokable command #20932

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: 7.3
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 4 additions & 10 deletions components/console/changing_default_command.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,14 @@ name to the ``setDefaultCommand()`` method::

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand(name: 'hello:world')]
#[AsCommand(name: 'hello:world', description: 'Outputs "Hello World"')]
class HelloWorldCommand extends Command
{
protected function configure(): void
public function __invoke(SymfonyStyle $io): int
{
$this->setDescription('Outputs "Hello World"');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$output->writeln('Hello World');
$io->writeln('Hello World');

return Command::SUCCESS;
}
Expand Down
26 changes: 11 additions & 15 deletions components/console/events.rst
Original file line number Diff line number Diff line change
Expand Up @@ -209,36 +209,32 @@ method::
for these constants to be available.

If you use the Console component inside a Symfony application, commands can
handle signals themselves. To do so, implement the
:class:`Symfony\\Component\\Console\\Command\\SignalableCommandInterface` and subscribe to one or more signals::
handle signals themselves. To do so, subscribe to :class:`Symfony\\Component\\Console\\Event\\ConsoleSignalEvent` event::

// src/Command/SomeCommand.php
// src/Command/MyCommand.php
namespace App\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\SignalableCommandInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;

class SomeCommand extends Command implements SignalableCommandInterface
#[AsCommand(name: 'app:my-command')]
class MyCommand
{
// ...

public function getSubscribedSignals(): array
#[AsEventListener(ConsoleSignalEvent::class)]
public function handleSignal(ConsoleSignalEvent $event): void
{
// return here any of the constants defined by PCNTL extension
return [\SIGINT, \SIGTERM];
}

public function handleSignal(int $signal): int|false
{
if (\SIGINT === $signal) {
// set here any of the constants defined by PCNTL extension
if (in_array($event->getHandlingSignal(), [\SIGINT, \SIGTERM], true)) {
// ...
}

// ...

// return an integer to set the exit code, or
// false to continue normal execution
return 0;
$event->setExitCode(0);
}
}

Expand Down
8 changes: 4 additions & 4 deletions components/console/helpers/cursor.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ of the output:
// src/Command/MyCommand.php
namespace App\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Cursor;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class MyCommand extends Command
#[AsCommand(name: 'my-command')]
class MyCommand
{
// ...

public function execute(InputInterface $input, OutputInterface $output): int
public function __invoke(OutputInterface $output): int
{
// ...

Expand Down
8 changes: 4 additions & 4 deletions components/console/helpers/questionhelper.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ Suppose you want to confirm an action before actually executing it. Add
the following to your 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;
use Symfony\Component\Console\Question\ConfirmationQuestion;

class YourCommand extends Command
#[AsCommand(name: 'app:my-command')]
class MyCommand
{
// ...

public function execute(InputInterface $input, OutputInterface $output): int
public function __invoke(InputInterface $input, OutputInterface $output): int
{
$helper = $this->getHelper('question');
$question = new ConfirmationQuestion('Continue with this action?', false);
Expand Down
12 changes: 7 additions & 5 deletions components/console/helpers/table.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,16 @@ When building a console application it may be useful to display tabular data:
To display a table, use :class:`Symfony\\Component\\Console\\Helper\\Table`,
set the headers, set the rows and then render the table::

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
// ...

class SomeCommand extends Command
#[AsCommand(name: 'app:my-command')]
class MyCommand
{
public function execute(InputInterface $input, OutputInterface $output): int
public function __invoke(OutputInterface $output): int
{
$table = new Table($output);
$table
Expand Down Expand Up @@ -445,9 +446,10 @@ The only requirement to append rows is that the table must be rendered inside a
use Symfony\Component\Console\Helper\Table;
// ...

class SomeCommand extends Command
#[AsCommand(name: 'app:my-command')]
class MyCommand
{
public function execute(InputInterface $input, OutputInterface $output): int
public function __invoke(OutputInterface $output): int
{
$section = $output->section();
$table = new Table($section);
Expand Down
11 changes: 3 additions & 8 deletions components/console/helpers/tree.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,17 @@ inside your console command::
namespace App\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\TreeHelper;
use Symfony\Component\Console\Helper\TreeNode;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand(name: 'app:some-command', description: '...')]
class SomeCommand extends Command
#[AsCommand(name: 'app:my-command', description: '...')]
class MyCommand
{
// ...

protected function execute(InputInterface $input, OutputInterface $output): int
public function __invoke(SymfonyStyle $io): int
{
$io = new SymfonyStyle($input, $output);

$node = TreeNode::fromValues([
'config/',
'public/',
Expand Down
5 changes: 2 additions & 3 deletions components/console/logger.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,16 @@ You can rely on the logger to use this dependency inside a command::
use Acme\MyDependency;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Logger\ConsoleLogger;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(
name: 'my:command',
description: 'Use an external dependency requiring a PSR-3 logger'
)]
class MyCommand extends Command
class MyCommand
{
protected function execute(InputInterface $input, OutputInterface $output): int
public function __invoke(OutputInterface $output): int
{
$logger = new ConsoleLogger($output);

Expand Down
11 changes: 5 additions & 6 deletions components/console/single_command_tool.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,18 @@ it is possible to remove this need by declaring a single command application::
<?php
require __DIR__.'/vendor/autoload.php';

use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Attribute\Argument;
use Symfony\Component\Console\Attribute\Option;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\SingleCommandApplication;

(new SingleCommandApplication())
->setName('My Super Command') // Optional
->setVersion('1.0.0') // Optional
->addArgument('foo', InputArgument::OPTIONAL, 'The directory')
->addOption('bar', null, InputOption::VALUE_REQUIRED)
->setCode(function (InputInterface $input, OutputInterface $output): int {
->setCode(function (OutputInterface $output, #[Argument] string $foo = 'The directory', #[Option] string $bar = ''): int {
// output arguments and options

return 0;
})
->run();

Expand Down
9 changes: 7 additions & 2 deletions components/process.rst
Original file line number Diff line number Diff line change
Expand Up @@ -430,11 +430,14 @@ However, if you run the command via the Symfony ``Process`` class, PHP will use
the settings defined in the ``php.ini`` file. You can solve this issue by using
the :class:`Symfony\\Component\\Process\\PhpSubprocess` class to run the command::

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Process\Process;

class MyCommand extends Command
#[AsCommand(name: 'app:my-command')]
class MyCommand
{
protected function execute(InputInterface $input, OutputInterface $output): int
public function __invoke(SymfonyStyle $io): int
{
// the memory_limit (and any other config option) of this command is
// the one defined in php.ini instead of the new values (optionally)
Expand All @@ -444,6 +447,8 @@ the :class:`Symfony\\Component\\Process\\PhpSubprocess` class to run the command
// the memory_limit (and any other config option) of this command takes
// into account the values (optionally) passed via the '-d' command option
$childProcess = new PhpSubprocess(['bin/console', 'cache:pool:prune']);

return 0;
}
}

Expand Down
32 changes: 15 additions & 17 deletions console.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,23 +110,19 @@ completion (by default, by pressing the Tab key).
Creating a Command
------------------

Commands are defined in classes extending
:class:`Symfony\\Component\\Console\\Command\\Command`. For example, you may
want a command to create a user::
Commands are defined in classes, for example, you may want a command to create a user::

// src/Command/CreateUserCommand.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;

// the name of the command is what users type after "php bin/console"
#[AsCommand(name: 'app:create-user')]
class CreateUserCommand extends Command
class CreateUserCommand
{
protected function execute(InputInterface $input, OutputInterface $output): int
public function __invoke(): int
{
// ... put here the code to create the user

Expand All @@ -147,6 +143,10 @@ want a command to create a user::
}
}

Additionally, can extend the :class:`Symfony\\Component\\Console\\Command\\Command` class to
leverage advanced features like lifecycle hooks: :method:`Symfony\\Component\\Console\\Command\\Command::initialize`,
:method:`Symfony\\Component\\Console\\Command\\Command::interact`, and built-in helpers.

Configuring the Command
~~~~~~~~~~~~~~~~~~~~~~~

Expand All @@ -156,18 +156,16 @@ You can optionally define a description, help message and the

// src/Command/CreateUserCommand.php

// ...
class CreateUserCommand extends Command
#[AsCommand(
name: 'app:create-user',
description: 'Creates a new user.', // the command description shown when running "php bin/console list"
help: 'This command allows you to create a user...', // the command help shown when running the command with the "--help" option
)]
class CreateUserCommand
{
// ...
protected function configure(): void
public function __invoke(): int
{
$this
// the command description shown when running "php bin/console list"
->setDescription('Creates a new user.')
// the command help shown when running the command with the "--help" option
->setHelp('This command allows you to create a user...')
;
// ...
}
}

Expand Down
9 changes: 4 additions & 5 deletions console/calling_commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,15 @@ the returned code from the command (return value from command ``execute()``
method)::

// ...
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class CreateUserCommand extends Command
#[AsCommand(name: 'app:create-user')]
class CreateUserCommand
{
// ...

protected function execute(InputInterface $input, OutputInterface $output): int
public function __invoke(OutputInterface $output): int
{
$greetInput = new ArrayInput([
// the command name is passed as first argument
Expand Down
19 changes: 4 additions & 15 deletions console/commands_as_services.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,16 @@ For example, suppose you want to log something from within your command::

use Psr\Log\LoggerInterface;
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:sunshine')]
class SunshineCommand extends Command
#[AsCommand(name: 'app:sunshine', description: 'Good morning!')]
class SunshineCommand
{
public function __construct(
private LoggerInterface $logger,
) {
// you *must* call the parent constructor
parent::__construct();
}

protected function configure(): void
{
$this
->setDescription('Good morning!');
}

protected function execute(InputInterface $input, OutputInterface $output): int
public function __invoke(): int
{
$this->logger->info('Waking up the sun');
// ...
Expand Down Expand Up @@ -70,7 +59,7 @@ To make your command lazily loaded, either define its name using the PHP
// ...

#[AsCommand(name: 'app:sunshine')]
class SunshineCommand extends Command
class SunshineCommand
{
// ...
}
Expand Down
3 changes: 1 addition & 2 deletions console/hide_commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ the ``hidden`` property of the ``AsCommand`` attribute::
namespace App\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;

#[AsCommand(name: 'app:legacy', hidden: true)]
class LegacyCommand extends Command
class LegacyCommand
{
// ...
}
Expand Down
Loading