diff --git a/src/Symfony/Bridge/Monolog/Command/ServerLogCommand.php b/src/Symfony/Bridge/Monolog/Command/ServerLogCommand.php
index f04db50c8ced7..8ecd220a2f0b9 100644
--- a/src/Symfony/Bridge/Monolog/Command/ServerLogCommand.php
+++ b/src/Symfony/Bridge/Monolog/Command/ServerLogCommand.php
@@ -34,6 +34,7 @@ class ServerLogCommand extends Command
private $handler;
protected static $defaultName = 'server:log';
+ protected static $defaultDescription = 'Starts a log server that displays logs in real time';
public function isEnabled()
{
@@ -60,7 +61,7 @@ protected function configure()
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The line format', ConsoleFormatter::SIMPLE_FORMAT)
->addOption('date-format', null, InputOption::VALUE_REQUIRED, 'The date format', ConsoleFormatter::SIMPLE_DATE)
->addOption('filter', null, InputOption::VALUE_REQUIRED, 'An expression to filter log. Example: "level > 200 or channel in [\'app\', \'doctrine\']"')
- ->setDescription('Starts a log server that displays logs in real time')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
%command.name% starts a log server to display in real time the log
messages generated by your application:
diff --git a/src/Symfony/Bridge/Twig/Command/DebugCommand.php b/src/Symfony/Bridge/Twig/Command/DebugCommand.php
index 8962a41dd366e..88fae85aaf102 100644
--- a/src/Symfony/Bridge/Twig/Command/DebugCommand.php
+++ b/src/Symfony/Bridge/Twig/Command/DebugCommand.php
@@ -33,6 +33,7 @@
class DebugCommand extends Command
{
protected static $defaultName = 'debug:twig';
+ protected static $defaultDescription = 'Shows a list of twig functions, filters, globals and tests';
private $twig;
private $projectDir;
@@ -60,7 +61,7 @@ protected function configure()
new InputOption('filter', null, InputOption::VALUE_REQUIRED, 'Show details for all entries matching this filter'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (text or json)', 'text'),
])
- ->setDescription('Shows a list of twig functions, filters, globals and tests')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% command outputs a list of twig functions,
filters, globals and tests.
diff --git a/src/Symfony/Bridge/Twig/Command/LintCommand.php b/src/Symfony/Bridge/Twig/Command/LintCommand.php
index 505f05959bb68..39d79f0b1bba6 100644
--- a/src/Symfony/Bridge/Twig/Command/LintCommand.php
+++ b/src/Symfony/Bridge/Twig/Command/LintCommand.php
@@ -35,6 +35,7 @@
class LintCommand extends Command
{
protected static $defaultName = 'lint:twig';
+ protected static $defaultDescription = 'Lints a template and outputs encountered errors';
private $twig;
@@ -48,7 +49,7 @@ public function __construct(Environment $twig)
protected function configure()
{
$this
- ->setDescription('Lints a template and outputs encountered errors')
+ ->setDescription(self::$defaultDescription)
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
->addOption('show-deprecations', null, InputOption::VALUE_NONE, 'Show deprecations as errors')
->addArgument('filename', InputArgument::IS_ARRAY, 'A file, a directory or "-" for reading from STDIN')
diff --git a/src/Symfony/Bundle/DebugBundle/Resources/config/services.php b/src/Symfony/Bundle/DebugBundle/Resources/config/services.php
index abde96d0625ec..d0f57c092872e 100644
--- a/src/Symfony/Bundle/DebugBundle/Resources/config/services.php
+++ b/src/Symfony/Bundle/DebugBundle/Resources/config/services.php
@@ -11,7 +11,9 @@
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+use Monolog\Formatter\FormatterInterface;
use Symfony\Bridge\Monolog\Command\ServerLogCommand;
+use Symfony\Bridge\Monolog\Formatter\ConsoleFormatter;
use Symfony\Bridge\Twig\Extension\DumpExtension;
use Symfony\Component\HttpKernel\DataCollector\DumpDataCollector;
use Symfony\Component\HttpKernel\EventListener\DumpListener;
@@ -127,9 +129,12 @@
'html' => inline_service(HtmlDescriptor::class)->args([service('var_dumper.html_dumper')]),
],
])
- ->tag('console.command', ['command' => 'server:dump'])
+ ->tag('console.command')
->set('monolog.command.server_log', ServerLogCommand::class)
- ->tag('console.command', ['command' => 'server:log'])
;
+
+ if (class_exists(ConsoleFormatter::class) && interface_exists(FormatterInterface::class)) {
+ $container->services()->get('monolog.command.server_log')->tag('console.command');
+ }
};
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php
index 4c9f968736c59..d1de7c651b9b1 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php
@@ -30,6 +30,7 @@
class AboutCommand extends Command
{
protected static $defaultName = 'about';
+ protected static $defaultDescription = 'Displays information about the current project';
/**
* {@inheritdoc}
@@ -37,7 +38,7 @@ class AboutCommand extends Command
protected function configure()
{
$this
- ->setDescription('Displays information about the current project')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOT'
The %command.name% command displays information about the current Symfony project.
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php
index 70ad92343eb25..ae8ca199879fc 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php
@@ -40,6 +40,7 @@ class AssetsInstallCommand extends Command
public const METHOD_RELATIVE_SYMLINK = 'relative symlink';
protected static $defaultName = 'assets:install';
+ protected static $defaultDescription = 'Installs bundles web assets under a public directory';
private $filesystem;
private $projectDir;
@@ -64,7 +65,7 @@ protected function configure()
->addOption('symlink', null, InputOption::VALUE_NONE, 'Symlinks the assets instead of copying it')
->addOption('relative', null, InputOption::VALUE_NONE, 'Make relative symlinks')
->addOption('no-cleanup', null, InputOption::VALUE_NONE, 'Do not remove the assets of the bundles that no longer exist')
- ->setDescription('Installs bundles web assets under a public directory')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOT'
The %command.name% command installs bundle assets into a given
directory (e.g. the public directory).
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php
index b9a829471f098..5b9271920e7ae 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php
@@ -36,6 +36,7 @@
class CacheClearCommand extends Command
{
protected static $defaultName = 'cache:clear';
+ protected static $defaultDescription = 'Clears the cache';
private $cacheClearer;
private $filesystem;
@@ -58,7 +59,7 @@ protected function configure()
new InputOption('no-warmup', '', InputOption::VALUE_NONE, 'Do not warm up the cache'),
new InputOption('no-optional-warmers', '', InputOption::VALUE_NONE, 'Skip optional cache warmers (faster)'),
])
- ->setDescription('Clears the cache')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% command clears the application cache for a given environment
and debug mode:
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolClearCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolClearCommand.php
index 50aacd9bbd73d..2ffde574f16b0 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolClearCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolClearCommand.php
@@ -28,6 +28,7 @@
final class CachePoolClearCommand extends Command
{
protected static $defaultName = 'cache:pool:clear';
+ protected static $defaultDescription = 'Clears cache pools';
private $poolClearer;
@@ -47,7 +48,7 @@ protected function configure()
->setDefinition([
new InputArgument('pools', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'A list of cache pools or cache pool clearers'),
])
- ->setDescription('Clears cache pools')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% command clears the given cache pools or cache pool clearers.
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolDeleteCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolDeleteCommand.php
index 2a7a2fe513040..6ac82925e6a3d 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolDeleteCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolDeleteCommand.php
@@ -26,6 +26,7 @@
final class CachePoolDeleteCommand extends Command
{
protected static $defaultName = 'cache:pool:delete';
+ protected static $defaultDescription = 'Deletes an item from a cache pool';
private $poolClearer;
@@ -46,7 +47,7 @@ protected function configure()
new InputArgument('pool', InputArgument::REQUIRED, 'The cache pool from which to delete an item'),
new InputArgument('key', InputArgument::REQUIRED, 'The cache key to delete from the pool'),
])
- ->setDescription('Deletes an item from a cache pool')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% deletes an item from a given cache pool.
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolListCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolListCommand.php
index 7b725411d5015..4a4b1eb2fa49e 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolListCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolListCommand.php
@@ -24,6 +24,7 @@
final class CachePoolListCommand extends Command
{
protected static $defaultName = 'cache:pool:list';
+ protected static $defaultDescription = 'List available cache pools';
private $poolNames;
@@ -40,7 +41,7 @@ public function __construct(array $poolNames)
protected function configure()
{
$this
- ->setDescription('List available cache pools')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% command lists all available cache pools.
EOF
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolPruneCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolPruneCommand.php
index 65f3ff6b5802e..bc5b7f861fb72 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolPruneCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolPruneCommand.php
@@ -25,6 +25,7 @@
final class CachePoolPruneCommand extends Command
{
protected static $defaultName = 'cache:pool:prune';
+ protected static $defaultDescription = 'Prunes cache pools';
private $pools;
@@ -44,7 +45,7 @@ public function __construct(iterable $pools)
protected function configure()
{
$this
- ->setDescription('Prunes cache pools')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% command deletes all expired items from all pruneable pools.
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php
index 8feb2dd9c51b2..a79a1106d6e6d 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php
@@ -29,6 +29,7 @@
class CacheWarmupCommand extends Command
{
protected static $defaultName = 'cache:warmup';
+ protected static $defaultDescription = 'Warms up an empty cache';
private $cacheWarmer;
@@ -48,7 +49,7 @@ protected function configure()
->setDefinition([
new InputOption('no-optional-warmers', '', InputOption::VALUE_NONE, 'Skip optional cache warmers (faster)'),
])
- ->setDescription('Warms up an empty cache')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% command warms up the cache.
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php
index c68f17e120bbd..8a526cb99143d 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php
@@ -33,6 +33,7 @@
class ConfigDebugCommand extends AbstractConfigCommand
{
protected static $defaultName = 'debug:config';
+ protected static $defaultDescription = 'Dumps the current configuration for an extension';
/**
* {@inheritdoc}
@@ -44,7 +45,7 @@ protected function configure()
new InputArgument('name', InputArgument::OPTIONAL, 'The bundle name or the extension alias'),
new InputArgument('path', InputArgument::OPTIONAL, 'The configuration option path'),
])
- ->setDescription('Dumps the current configuration for an extension')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% command dumps the current configuration for an
extension/bundle.
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php
index 62dcc856e0f56..c1b0cf1626c19 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php
@@ -36,6 +36,7 @@
class ConfigDumpReferenceCommand extends AbstractConfigCommand
{
protected static $defaultName = 'config:dump-reference';
+ protected static $defaultDescription = 'Dumps the default configuration for an extension';
/**
* {@inheritdoc}
@@ -48,7 +49,7 @@ protected function configure()
new InputArgument('path', InputArgument::OPTIONAL, 'The configuration option path'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (yaml or xml)', 'yaml'),
])
- ->setDescription('Dumps the default configuration for an extension')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% command dumps the default configuration for an
extension/bundle.
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php
index 7c330dbdf4f85..8b41fc1dce7a0 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php
@@ -35,6 +35,7 @@ class ContainerDebugCommand extends Command
use BuildDebugContainerTrait;
protected static $defaultName = 'debug:container';
+ protected static $defaultDescription = 'Displays current services for an application';
/**
* {@inheritdoc}
@@ -57,7 +58,7 @@ protected function configure()
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw description'),
new InputOption('deprecations', null, InputOption::VALUE_NONE, 'Displays deprecations generated when compiling and warming up the container'),
])
- ->setDescription('Displays current services for an application')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% command displays all configured public services:
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php
index f059df1ee62fe..02ef668cfbee6 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php
@@ -30,6 +30,7 @@
final class ContainerLintCommand extends Command
{
protected static $defaultName = 'lint:container';
+ protected static $defaultDescription = 'Ensures that arguments injected into services match type declarations';
/**
* @var ContainerBuilder
@@ -42,7 +43,7 @@ final class ContainerLintCommand extends Command
protected function configure()
{
$this
- ->setDescription('Ensures that arguments injected into services match type declarations')
+ ->setDescription(self::$defaultDescription)
->setHelp('This command parses service definitions and ensures that injected values match the type declarations of each services\' class.')
;
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php
index 32bd630f32516..8aceb5c0a66df 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php
@@ -30,6 +30,7 @@
class DebugAutowiringCommand extends ContainerDebugCommand
{
protected static $defaultName = 'debug:autowiring';
+ protected static $defaultDescription = 'Lists classes/interfaces you can use for autowiring';
private $supportsHref;
private $fileLinkFormatter;
@@ -50,7 +51,7 @@ protected function configure()
new InputArgument('search', InputArgument::OPTIONAL, 'A search filter'),
new InputOption('all', null, InputOption::VALUE_NONE, 'Show also services that are not aliased'),
])
- ->setDescription('Lists classes/interfaces you can use for autowiring')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% command displays the classes and interfaces that
you can use as type-hints for autowiring:
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/EventDispatcherDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/EventDispatcherDebugCommand.php
index a053d96dd8fdb..b828dee78139e 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/EventDispatcherDebugCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/EventDispatcherDebugCommand.php
@@ -33,6 +33,7 @@ class EventDispatcherDebugCommand extends Command
private const DEFAULT_DISPATCHER = 'event_dispatcher';
protected static $defaultName = 'debug:event-dispatcher';
+ protected static $defaultDescription = 'Displays configured listeners for an application';
private $dispatchers;
public function __construct(ContainerInterface $dispatchers)
@@ -54,7 +55,7 @@ protected function configure()
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw description'),
])
- ->setDescription('Displays configured listeners for an application')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% command displays all configured listeners:
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php
index 16acf7a7db9c4..24c306c9d901d 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php
@@ -36,6 +36,7 @@ class RouterDebugCommand extends Command
use BuildDebugContainerTrait;
protected static $defaultName = 'debug:router';
+ protected static $defaultDescription = 'Displays current routes for an application';
private $router;
private $fileLinkFormatter;
@@ -59,7 +60,7 @@ protected function configure()
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw route(s)'),
])
- ->setDescription('Displays current routes for an application')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% displays the configured routes:
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php
index 1e2fefbbacb26..85ce52608d0d4 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php
@@ -31,6 +31,7 @@
class RouterMatchCommand extends Command
{
protected static $defaultName = 'router:match';
+ protected static $defaultDescription = 'Helps debug routes by simulating a path info match';
private $router;
private $expressionLanguageProviders;
@@ -55,7 +56,7 @@ protected function configure()
new InputOption('scheme', null, InputOption::VALUE_REQUIRED, 'Sets the URI scheme (usually http or https)'),
new InputOption('host', null, InputOption::VALUE_REQUIRED, 'Sets the URI host'),
])
- ->setDescription('Helps debug routes by simulating a path info match')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% shows which routes match a given request and which don't and for what reason:
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsDecryptToLocalCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsDecryptToLocalCommand.php
index 1571c7f1b7c79..0ca168b5fd177 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsDecryptToLocalCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsDecryptToLocalCommand.php
@@ -27,6 +27,7 @@
final class SecretsDecryptToLocalCommand extends Command
{
protected static $defaultName = 'secrets:decrypt-to-local';
+ protected static $defaultDescription = 'Decrypts all secrets and stores them in the local vault';
private $vault;
private $localVault;
@@ -42,7 +43,7 @@ public function __construct(AbstractVault $vault, AbstractVault $localVault = nu
protected function configure()
{
$this
- ->setDescription('Decrypts all secrets and stores them in the local vault')
+ ->setDescription(self::$defaultDescription)
->addOption('force', 'f', InputOption::VALUE_NONE, 'Forces overriding of secrets that already exist in the local vault')
->setHelp(<<<'EOF'
The %command.name% command decrypts all secrets and copies them in the local vault.
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsEncryptFromLocalCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsEncryptFromLocalCommand.php
index e1c8904c698b5..d9a635f210391 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsEncryptFromLocalCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsEncryptFromLocalCommand.php
@@ -26,6 +26,7 @@
final class SecretsEncryptFromLocalCommand extends Command
{
protected static $defaultName = 'secrets:encrypt-from-local';
+ protected static $defaultDescription = 'Encrypts all local secrets to the vault';
private $vault;
private $localVault;
@@ -41,7 +42,7 @@ public function __construct(AbstractVault $vault, AbstractVault $localVault = nu
protected function configure()
{
$this
- ->setDescription('Encrypts all local secrets to the vault')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% command encrypts all locally overridden secrets to the vault.
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsGenerateKeysCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsGenerateKeysCommand.php
index 18dba29ac9797..abd052918e2eb 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsGenerateKeysCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsGenerateKeysCommand.php
@@ -29,6 +29,7 @@
final class SecretsGenerateKeysCommand extends Command
{
protected static $defaultName = 'secrets:generate-keys';
+ protected static $defaultDescription = 'Generates new encryption keys';
private $vault;
private $localVault;
@@ -44,7 +45,7 @@ public function __construct(AbstractVault $vault, AbstractVault $localVault = nu
protected function configure()
{
$this
- ->setDescription('Generates new encryption keys')
+ ->setDescription(self::$defaultDescription)
->addOption('local', 'l', InputOption::VALUE_NONE, 'Updates the local vault.')
->addOption('rotate', 'r', InputOption::VALUE_NONE, 'Re-encrypts existing secrets with the newly generated keys.')
->setHelp(<<<'EOF'
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsListCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsListCommand.php
index 9848ab993331e..f828776b16da3 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsListCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsListCommand.php
@@ -30,6 +30,7 @@
final class SecretsListCommand extends Command
{
protected static $defaultName = 'secrets:list';
+ protected static $defaultDescription = 'Lists all secrets';
private $vault;
private $localVault;
@@ -45,7 +46,7 @@ public function __construct(AbstractVault $vault, AbstractVault $localVault = nu
protected function configure()
{
$this
- ->setDescription('Lists all secrets')
+ ->setDescription(self::$defaultDescription)
->addOption('reveal', 'r', InputOption::VALUE_NONE, 'Display decrypted values alongside names')
->setHelp(<<<'EOF'
The %command.name% command list all stored secrets.
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsRemoveCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsRemoveCommand.php
index 2f06cb4592545..1ea079b3dc338 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsRemoveCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsRemoveCommand.php
@@ -29,6 +29,7 @@
final class SecretsRemoveCommand extends Command
{
protected static $defaultName = 'secrets:remove';
+ protected static $defaultDescription = 'Removes a secret from the vault';
private $vault;
private $localVault;
@@ -44,7 +45,7 @@ public function __construct(AbstractVault $vault, AbstractVault $localVault = nu
protected function configure()
{
$this
- ->setDescription('Removes a secret from the vault')
+ ->setDescription(self::$defaultDescription)
->addArgument('name', InputArgument::REQUIRED, 'The name of the secret')
->addOption('local', 'l', InputOption::VALUE_NONE, 'Updates the local vault.')
->setHelp(<<<'EOF'
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsSetCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsSetCommand.php
index 95b8f6a0f622b..3753691a7379b 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsSetCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsSetCommand.php
@@ -30,6 +30,7 @@
final class SecretsSetCommand extends Command
{
protected static $defaultName = 'secrets:set';
+ protected static $defaultDescription = 'Sets a secret in the vault';
private $vault;
private $localVault;
@@ -45,7 +46,7 @@ public function __construct(AbstractVault $vault, AbstractVault $localVault = nu
protected function configure()
{
$this
- ->setDescription('Sets a secret in the vault')
+ ->setDescription(self::$defaultDescription)
->addArgument('name', InputArgument::REQUIRED, 'The name of the secret')
->addArgument('file', InputArgument::OPTIONAL, 'A file where to read the secret from or "-" for reading from STDIN')
->addOption('local', 'l', InputOption::VALUE_NONE, 'Updates the local vault.')
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php
index ec17387d9b078..8cb80babe61cb 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php
@@ -47,6 +47,7 @@ class TranslationDebugCommand extends Command
public const MESSAGE_EQUALS_FALLBACK = 2;
protected static $defaultName = 'debug:translation';
+ protected static $defaultDescription = 'Displays translation messages information';
private $translator;
private $reader;
@@ -83,7 +84,7 @@ protected function configure()
new InputOption('only-unused', null, InputOption::VALUE_NONE, 'Displays only unused messages'),
new InputOption('all', null, InputOption::VALUE_NONE, 'Load messages from all registered bundles'),
])
- ->setDescription('Displays translation messages information')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% command helps finding unused or missing translation
messages and comparing them with the fallback ones by inspecting the
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php
index 9274d7e70baf6..2e4dd776b779b 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php
@@ -42,6 +42,7 @@ class TranslationUpdateCommand extends Command
private const SORT_ORDERS = [self::ASC, self::DESC];
protected static $defaultName = 'translation:update';
+ protected static $defaultDescription = 'Updates the translation file';
private $writer;
private $reader;
@@ -85,7 +86,7 @@ protected function configure()
new InputOption('sort', null, InputOption::VALUE_OPTIONAL, 'Return list of messages sorted alphabetically', 'asc'),
new InputOption('as-tree', null, InputOption::VALUE_OPTIONAL, 'Dump the messages as a tree-like structure: The given value defines the level where to switch to inline YAML'),
])
- ->setDescription('Updates the translation file')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% command extracts translation strings from templates
of a given bundle or the default translations directory. It can display them or merge
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php
index cec930da1c0da..d3cc460318e4b 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php
@@ -30,6 +30,7 @@
class WorkflowDumpCommand extends Command
{
protected static $defaultName = 'workflow:dump';
+ protected static $defaultDescription = 'Dump a workflow';
/**
* {@inheritdoc}
@@ -43,7 +44,7 @@ protected function configure()
new InputOption('label', 'l', InputOption::VALUE_REQUIRED, 'Labels a graph'),
new InputOption('dump-format', null, InputOption::VALUE_REQUIRED, 'The dump format [dot|puml]', 'dot'),
])
- ->setDescription('Dump a workflow')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% command dumps the graphical representation of a
workflow in different formats
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php
index 81531599f1f6c..5aea86ba7a06b 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php
@@ -62,76 +62,76 @@
->tag('kernel.event_subscriber')
->set('console.command.about', AboutCommand::class)
- ->tag('console.command', ['command' => 'about'])
+ ->tag('console.command')
->set('console.command.assets_install', AssetsInstallCommand::class)
->args([
service('filesystem'),
param('kernel.project_dir'),
])
- ->tag('console.command', ['command' => 'assets:install'])
+ ->tag('console.command')
->set('console.command.cache_clear', CacheClearCommand::class)
->args([
service('cache_clearer'),
service('filesystem'),
])
- ->tag('console.command', ['command' => 'cache:clear'])
+ ->tag('console.command')
->set('console.command.cache_pool_clear', CachePoolClearCommand::class)
->args([
service('cache.global_clearer'),
])
- ->tag('console.command', ['command' => 'cache:pool:clear'])
+ ->tag('console.command')
->set('console.command.cache_pool_prune', CachePoolPruneCommand::class)
->args([
[],
])
- ->tag('console.command', ['command' => 'cache:pool:prune'])
+ ->tag('console.command')
->set('console.command.cache_pool_delete', CachePoolDeleteCommand::class)
->args([
service('cache.global_clearer'),
])
- ->tag('console.command', ['command' => 'cache:pool:delete'])
+ ->tag('console.command')
->set('console.command.cache_pool_list', CachePoolListCommand::class)
->args([
null,
])
- ->tag('console.command', ['command' => 'cache:pool:list'])
+ ->tag('console.command')
->set('console.command.cache_warmup', CacheWarmupCommand::class)
->args([
service('cache_warmer'),
])
- ->tag('console.command', ['command' => 'cache:warmup'])
+ ->tag('console.command')
->set('console.command.config_debug', ConfigDebugCommand::class)
- ->tag('console.command', ['command' => 'debug:config'])
+ ->tag('console.command')
->set('console.command.config_dump_reference', ConfigDumpReferenceCommand::class)
- ->tag('console.command', ['command' => 'config:dump-reference'])
+ ->tag('console.command')
->set('console.command.container_debug', ContainerDebugCommand::class)
- ->tag('console.command', ['command' => 'debug:container'])
+ ->tag('console.command')
->set('console.command.container_lint', ContainerLintCommand::class)
- ->tag('console.command', ['command' => 'lint:container'])
+ ->tag('console.command')
->set('console.command.debug_autowiring', DebugAutowiringCommand::class)
->args([
null,
service('debug.file_link_formatter')->nullOnInvalid(),
])
- ->tag('console.command', ['command' => 'debug:autowiring'])
+ ->tag('console.command')
->set('console.command.event_dispatcher_debug', EventDispatcherDebugCommand::class)
->args([
tagged_locator('event_dispatcher.dispatcher'),
])
- ->tag('console.command', ['command' => 'debug:event-dispatcher'])
+ ->tag('console.command')
->set('console.command.messenger_consume_messages', ConsumeMessagesCommand::class)
->args([
@@ -141,7 +141,7 @@
service('logger')->nullOnInvalid(),
[], // Receiver names
])
- ->tag('console.command', ['command' => 'messenger:consume'])
+ ->tag('console.command')
->tag('monolog.logger', ['channel' => 'messenger'])
->set('console.command.messenger_setup_transports', SetupTransportsCommand::class)
@@ -149,19 +149,19 @@
service('messenger.receiver_locator'),
[], // Receiver names
])
- ->tag('console.command', ['command' => 'messenger:setup-transports'])
+ ->tag('console.command')
->set('console.command.messenger_debug', DebugCommand::class)
->args([
[], // Message to handlers mapping
])
- ->tag('console.command', ['command' => 'debug:messenger'])
+ ->tag('console.command')
->set('console.command.messenger_stop_workers', StopWorkersCommand::class)
->args([
service('cache.messenger.restart_workers_signal'),
])
- ->tag('console.command', ['command' => 'messenger:stop-workers'])
+ ->tag('console.command')
->set('console.command.messenger_failed_messages_retry', FailedMessagesRetryCommand::class)
->args([
@@ -171,35 +171,35 @@
service('event_dispatcher'),
service('logger'),
])
- ->tag('console.command', ['command' => 'messenger:failed:retry'])
+ ->tag('console.command')
->set('console.command.messenger_failed_messages_show', FailedMessagesShowCommand::class)
->args([
abstract_arg('Receiver name'),
abstract_arg('Receiver'),
])
- ->tag('console.command', ['command' => 'messenger:failed:show'])
+ ->tag('console.command')
->set('console.command.messenger_failed_messages_remove', FailedMessagesRemoveCommand::class)
->args([
abstract_arg('Receiver name'),
abstract_arg('Receiver'),
])
- ->tag('console.command', ['command' => 'messenger:failed:remove'])
+ ->tag('console.command')
->set('console.command.router_debug', RouterDebugCommand::class)
->args([
service('router'),
service('debug.file_link_formatter')->nullOnInvalid(),
])
- ->tag('console.command', ['command' => 'debug:router'])
+ ->tag('console.command')
->set('console.command.router_match', RouterMatchCommand::class)
->args([
service('router'),
tagged_iterator('routing.expression_language_provider'),
])
- ->tag('console.command', ['command' => 'router:match'])
+ ->tag('console.command')
->set('console.command.translation_debug', TranslationDebugCommand::class)
->args([
@@ -211,7 +211,7 @@
[], // Translator paths
[], // Twig paths
])
- ->tag('console.command', ['command' => 'debug:translation'])
+ ->tag('console.command')
->set('console.command.translation_update', TranslationUpdateCommand::class)
->args([
@@ -224,22 +224,22 @@
[], // Translator paths
[], // Twig paths
])
- ->tag('console.command', ['command' => 'translation:update'])
+ ->tag('console.command')
->set('console.command.validator_debug', ValidatorDebugCommand::class)
->args([
service('validator'),
])
- ->tag('console.command', ['command' => 'debug:validator'])
+ ->tag('console.command')
->set('console.command.workflow_dump', WorkflowDumpCommand::class)
- ->tag('console.command', ['command' => 'workflow:dump'])
+ ->tag('console.command')
->set('console.command.xliff_lint', XliffLintCommand::class)
- ->tag('console.command', ['command' => 'lint:xliff'])
+ ->tag('console.command')
->set('console.command.yaml_lint', YamlLintCommand::class)
- ->tag('console.command', ['command' => 'lint:yaml'])
+ ->tag('console.command')
->set('console.command.form_debug', \Symfony\Component\Form\Command\DebugCommand::class)
->args([
@@ -250,48 +250,48 @@
[], // All type guessers are stored here by FormPass
service('debug.file_link_formatter')->nullOnInvalid(),
])
- ->tag('console.command', ['command' => 'debug:form'])
+ ->tag('console.command')
->set('console.command.secrets_set', SecretsSetCommand::class)
->args([
service('secrets.vault'),
service('secrets.local_vault')->nullOnInvalid(),
])
- ->tag('console.command', ['command' => 'secrets:set'])
+ ->tag('console.command')
->set('console.command.secrets_remove', SecretsRemoveCommand::class)
->args([
service('secrets.vault'),
service('secrets.local_vault')->nullOnInvalid(),
])
- ->tag('console.command', ['command' => 'secrets:remove'])
+ ->tag('console.command')
->set('console.command.secrets_generate_key', SecretsGenerateKeysCommand::class)
->args([
service('secrets.vault'),
service('secrets.local_vault')->ignoreOnInvalid(),
])
- ->tag('console.command', ['command' => 'secrets:generate-keys'])
+ ->tag('console.command')
->set('console.command.secrets_list', SecretsListCommand::class)
->args([
service('secrets.vault'),
service('secrets.local_vault'),
])
- ->tag('console.command', ['command' => 'secrets:list'])
+ ->tag('console.command')
->set('console.command.secrets_decrypt_to_local', SecretsDecryptToLocalCommand::class)
->args([
service('secrets.vault'),
service('secrets.local_vault')->ignoreOnInvalid(),
])
- ->tag('console.command', ['command' => 'secrets:decrypt-to-local'])
+ ->tag('console.command')
->set('console.command.secrets_encrypt_from_local', SecretsEncryptFromLocalCommand::class)
->args([
service('secrets.vault'),
service('secrets.local_vault'),
])
- ->tag('console.command', ['command' => 'secrets:encrypt-from-local'])
+ ->tag('console.command')
;
};
diff --git a/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php b/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php
index de23fdd618a11..8352bc41a8b4c 100644
--- a/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php
+++ b/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php
@@ -34,6 +34,7 @@
class UserPasswordEncoderCommand extends Command
{
protected static $defaultName = 'security:encode-password';
+ protected static $defaultDescription = 'Encodes a password';
private $encoderFactory;
private $userClasses;
@@ -52,7 +53,7 @@ public function __construct(EncoderFactoryInterface $encoderFactory, array $user
protected function configure()
{
$this
- ->setDescription('Encodes a password')
+ ->setDescription(self::$defaultDescription)
->addArgument('password', InputArgument::OPTIONAL, 'The plain password to encode.')
->addArgument('user-class', InputArgument::OPTIONAL, 'The User entity class path associated with the encoder used to encode the password.')
->addOption('empty-salt', null, InputOption::VALUE_NONE, 'Do not generate a salt or let the encoder generate one.')
diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/console.php b/src/Symfony/Bundle/SecurityBundle/Resources/config/console.php
index a5ea6868a8bb6..61bc1f553e582 100644
--- a/src/Symfony/Bundle/SecurityBundle/Resources/config/console.php
+++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/console.php
@@ -20,6 +20,6 @@
service('security.encoder_factory'),
abstract_arg('encoders user classes'),
])
- ->tag('console.command', ['command' => 'security:encode-password'])
+ ->tag('console.command')
;
};
diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/console.php b/src/Symfony/Bundle/TwigBundle/Resources/config/console.php
index 9abd75da19ffc..0dc7ebdb7a5ad 100644
--- a/src/Symfony/Bundle/TwigBundle/Resources/config/console.php
+++ b/src/Symfony/Bundle/TwigBundle/Resources/config/console.php
@@ -24,10 +24,10 @@
param('twig.default_path'),
service('debug.file_link_formatter')->nullOnInvalid(),
])
- ->tag('console.command', ['command' => 'debug:twig'])
+ ->tag('console.command')
->set('twig.command.lint', LintCommand::class)
->args([service('twig')])
- ->tag('console.command', ['command' => 'lint:twig'])
+ ->tag('console.command')
;
};
diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php
index 1b995abb20a59..440b93dc5c6fe 100644
--- a/src/Symfony/Component/Console/Application.php
+++ b/src/Symfony/Component/Console/Application.php
@@ -13,6 +13,7 @@
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\HelpCommand;
+use Symfony\Component\Console\Command\LazyCommand;
use Symfony\Component\Console\Command\ListCommand;
use Symfony\Component\Console\Command\SignalableCommandInterface;
use Symfony\Component\Console\CommandLoader\CommandLoaderInterface;
@@ -489,8 +490,10 @@ public function add(Command $command)
return null;
}
- // Will throw if the command is not correctly initialized.
- $command->getDefinition();
+ if (!$command instanceof LazyCommand) {
+ // Will throw if the command is not correctly initialized.
+ $command->getDefinition();
+ }
if (!$command->getName()) {
throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_debug_type($command)));
diff --git a/src/Symfony/Component/Console/CHANGELOG.md b/src/Symfony/Component/Console/CHANGELOG.md
index 17a709a6e27e7..37689ab692db1 100644
--- a/src/Symfony/Component/Console/CHANGELOG.md
+++ b/src/Symfony/Component/Console/CHANGELOG.md
@@ -4,8 +4,10 @@ CHANGELOG
5.3
---
- * Added `GithubActionReporter` to render annotations in a Github Action
- * Added `InputOption::VALUE_NEGATABLE` flag to handle `--foo`/`--no-foo` options.
+ * Add `GithubActionReporter` to render annotations in a Github Action
+ * Add `InputOption::VALUE_NEGATABLE` flag to handle `--foo`/`--no-foo` options
+ * Add the `Command::$defaultDescription` static property and the `description` attribute
+ on the `console.command` tag to allow the `list` command to instantiate commands lazily
5.2.0
-----
diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php
index b530f4d89fd2e..a1809acd2322a 100644
--- a/src/Symfony/Component/Console/Command/Command.php
+++ b/src/Symfony/Component/Console/Command/Command.php
@@ -39,6 +39,11 @@ class Command
*/
protected static $defaultName;
+ /**
+ * @var string|null The default command description
+ */
+ protected static $defaultDescription;
+
private $application;
private $name;
private $processTitle;
@@ -65,6 +70,17 @@ public static function getDefaultName()
return $class === $r->class ? static::$defaultName : null;
}
+ /**
+ * @return string|null The default command description or null when no default description is set
+ */
+ public static function getDefaultDescription(): ?string
+ {
+ $class = static::class;
+ $r = new \ReflectionProperty($class, 'defaultDescription');
+
+ return $class === $r->class ? static::$defaultDescription : null;
+ }
+
/**
* @param string|null $name The name of the command; passing null means it must be set in configure()
*
@@ -78,6 +94,10 @@ public function __construct(string $name = null)
$this->setName($name);
}
+ if ('' === $this->description) {
+ $this->setDescription(static::getDefaultDescription() ?? '');
+ }
+
$this->configure();
}
@@ -298,6 +318,8 @@ public function setCode(callable $code)
* This method is not part of public API and should not be used directly.
*
* @param bool $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments
+ *
+ * @internal
*/
public function mergeApplicationDefinition(bool $mergeArgs = true)
{
@@ -554,11 +576,14 @@ public function getProcessedHelp()
*/
public function setAliases(iterable $aliases)
{
+ $list = [];
+
foreach ($aliases as $alias) {
$this->validateName($alias);
+ $list[] = $alias;
}
- $this->aliases = $aliases;
+ $this->aliases = \is_array($aliases) ? $aliases : $list;
return $this;
}
diff --git a/src/Symfony/Component/Console/Command/LazyCommand.php b/src/Symfony/Component/Console/Command/LazyCommand.php
new file mode 100644
index 0000000000000..763133e81e12c
--- /dev/null
+++ b/src/Symfony/Component/Console/Command/LazyCommand.php
@@ -0,0 +1,211 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Console\Command;
+
+use Symfony\Component\Console\Application;
+use Symfony\Component\Console\Helper\HelperSet;
+use Symfony\Component\Console\Input\InputDefinition;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * @author Nicolas Grekas
+ */
+final class LazyCommand extends Command
+{
+ private $command;
+ private $isEnabled;
+
+ public function __construct(string $name, array $aliases, string $description, bool $isHidden, \Closure $commandFactory, ?bool $isEnabled = true)
+ {
+ $this->setName($name)
+ ->setAliases($aliases)
+ ->setHidden($isHidden)
+ ->setDescription($description);
+
+ $this->command = $commandFactory;
+ $this->isEnabled = $isEnabled;
+ }
+
+ public function ignoreValidationErrors(): void
+ {
+ $this->getCommand()->ignoreValidationErrors();
+ }
+
+ public function setApplication(Application $application = null): void
+ {
+ if ($this->command instanceof parent) {
+ $this->command->setApplication($application);
+ }
+
+ parent::setApplication($application);
+ }
+
+ public function setHelperSet(HelperSet $helperSet): void
+ {
+ if ($this->command instanceof parent) {
+ $this->command->setHelperSet($helperSet);
+ }
+
+ parent::setHelperSet($helperSet);
+ }
+
+ public function isEnabled(): bool
+ {
+ return $this->isEnabled ?? $this->getCommand()->isEnabled();
+ }
+
+ public function run(InputInterface $input, OutputInterface $output): int
+ {
+ return $this->getCommand()->run($input, $output);
+ }
+
+ /**
+ * @return $this
+ */
+ public function setCode(callable $code): self
+ {
+ $this->getCommand()->setCode($code);
+
+ return $this;
+ }
+
+ /**
+ * @internal
+ */
+ public function mergeApplicationDefinition(bool $mergeArgs = true): void
+ {
+ $this->getCommand()->mergeApplicationDefinition($mergeArgs);
+ }
+
+ /**
+ * @return $this
+ */
+ public function setDefinition($definition): self
+ {
+ $this->getCommand()->setDefinition($definition);
+
+ return $this;
+ }
+
+ public function getDefinition(): InputDefinition
+ {
+ return $this->getCommand()->getDefinition();
+ }
+
+ public function getNativeDefinition(): InputDefinition
+ {
+ return $this->getCommand()->getNativeDefinition();
+ }
+
+ /**
+ * @return $this
+ */
+ public function addArgument(string $name, int $mode = null, string $description = '', $default = null): self
+ {
+ $this->getCommand()->addArgument($name, $mode, $description, $default);
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function addOption(string $name, $shortcut = null, int $mode = null, string $description = '', $default = null): self
+ {
+ $this->getCommand()->addOption($name, $shortcut, $mode, $description, $default);
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function setProcessTitle(string $title): self
+ {
+ $this->getCommand()->setProcessTitle($title);
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function setHelp(string $help): self
+ {
+ $this->getCommand()->setHelp($help);
+
+ return $this;
+ }
+
+ public function getHelp(): string
+ {
+ return $this->getCommand()->getHelp();
+ }
+
+ public function getProcessedHelp(): string
+ {
+ return $this->getCommand()->getProcessedHelp();
+ }
+
+ public function getSynopsis(bool $short = false): string
+ {
+ return $this->getCommand()->getSynopsis($short);
+ }
+
+ /**
+ * @return $this
+ */
+ public function addUsage(string $usage): self
+ {
+ $this->getCommand()->addUsage($usage);
+
+ return $this;
+ }
+
+ public function getUsages(): array
+ {
+ return $this->getCommand()->getUsages();
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getHelper(string $name)
+ {
+ return $this->getCommand()->getHelper($name);
+ }
+
+ public function getCommand(): parent
+ {
+ if (!$this->command instanceof \Closure) {
+ return $this->command;
+ }
+
+ $command = $this->command = ($this->command)();
+ $command->setApplication($this->getApplication());
+
+ if (null !== $this->getHelperSet()) {
+ $command->setHelperSet($this->getHelperSet());
+ }
+
+ $command->setName($this->getName())
+ ->setAliases($this->getAliases())
+ ->setHidden($this->isHidden())
+ ->setDescription($this->getDescription());
+
+ // Will throw if the command is not correctly initialized.
+ $command->getDefinition();
+
+ return $command;
+ }
+}
diff --git a/src/Symfony/Component/Console/DependencyInjection/AddConsoleCommandPass.php b/src/Symfony/Component/Console/DependencyInjection/AddConsoleCommandPass.php
index 77ae6f9d47869..42ec2eabad472 100644
--- a/src/Symfony/Component/Console/DependencyInjection/AddConsoleCommandPass.php
+++ b/src/Symfony/Component/Console/DependencyInjection/AddConsoleCommandPass.php
@@ -12,11 +12,14 @@
namespace Symfony\Component\Console\DependencyInjection;
use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Command\LazyCommand;
use Symfony\Component\Console\CommandLoader\ContainerCommandLoader;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\TypedReference;
/**
@@ -52,7 +55,7 @@ public function process(ContainerBuilder $container)
$class = $container->getParameterBag()->resolveValue($definition->getClass());
if (isset($tags[0]['command'])) {
- $commandName = $tags[0]['command'];
+ $aliases = $tags[0]['command'];
} else {
if (!$r = $container->getReflectionClass($class)) {
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
@@ -60,7 +63,14 @@ public function process(ContainerBuilder $container)
if (!$r->isSubclassOf(Command::class)) {
throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, $this->commandTag, Command::class));
}
- $commandName = $class::getDefaultName();
+ $aliases = $class::getDefaultName();
+ }
+
+ $aliases = explode('|', $aliases ?? '');
+ $commandName = array_shift($aliases);
+
+ if ($isHidden = '' === $commandName) {
+ $commandName = array_shift($aliases);
}
if (null === $commandName) {
@@ -74,16 +84,19 @@ public function process(ContainerBuilder $container)
continue;
}
+ $description = $tags[0]['description'] ?? null;
+
unset($tags[0]);
$lazyCommandMap[$commandName] = $id;
$lazyCommandRefs[$id] = new TypedReference($id, $class);
- $aliases = [];
foreach ($tags as $tag) {
if (isset($tag['command'])) {
$aliases[] = $tag['command'];
$lazyCommandMap[$tag['command']] = $id;
}
+
+ $description = $description ?? $tag['description'] ?? null;
}
$definition->addMethodCall('setName', [$commandName]);
@@ -91,6 +104,29 @@ public function process(ContainerBuilder $container)
if ($aliases) {
$definition->addMethodCall('setAliases', [$aliases]);
}
+
+ if ($isHidden) {
+ $definition->addMethodCall('setHidden', [true]);
+ }
+
+ if (!$description) {
+ if (!$r = $container->getReflectionClass($class)) {
+ throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
+ }
+ if (!$r->isSubclassOf(Command::class)) {
+ throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, $this->commandTag, Command::class));
+ }
+ $description = $class::getDefaultDescription();
+ }
+
+ if ($description) {
+ $definition->addMethodCall('setDescription', [$description]);
+
+ $container->register('.'.$id.'.lazy', LazyCommand::class)
+ ->setArguments([$commandName, $aliases, $description, $isHidden, new ServiceClosureArgument($lazyCommandRefs[$id])]);
+
+ $lazyCommandRefs[$id] = new Reference('.'.$id.'.lazy');
+ }
}
$container
diff --git a/src/Symfony/Component/Console/Tests/DependencyInjection/AddConsoleCommandPassTest.php b/src/Symfony/Component/Console/Tests/DependencyInjection/AddConsoleCommandPassTest.php
index 5e59f8fab3746..c0ecacd451e1d 100644
--- a/src/Symfony/Component/Console/Tests/DependencyInjection/AddConsoleCommandPassTest.php
+++ b/src/Symfony/Component/Console/Tests/DependencyInjection/AddConsoleCommandPassTest.php
@@ -13,6 +13,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Command\LazyCommand;
use Symfony\Component\Console\CommandLoader\ContainerCommandLoader;
use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
@@ -20,6 +21,7 @@
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\TypedReference;
class AddConsoleCommandPassTest extends TestCase
@@ -118,6 +120,39 @@ public function visibilityProvider()
];
}
+ public function testProcessFallsBackToDefaultDescription()
+ {
+ $container = new ContainerBuilder();
+ $container
+ ->register('with-defaults', DescribedCommand::class)
+ ->addTag('console.command')
+ ;
+
+ $pass = new AddConsoleCommandPass();
+ $pass->process($container);
+
+ $commandLoader = $container->getDefinition('console.command_loader');
+ $commandLocator = $container->getDefinition((string) $commandLoader->getArgument(0));
+
+ $this->assertSame(ContainerCommandLoader::class, $commandLoader->getClass());
+ $this->assertSame(['cmdname' => 'with-defaults'], $commandLoader->getArgument(1));
+ $this->assertEquals([['with-defaults' => new ServiceClosureArgument(new Reference('.with-defaults.lazy'))]], $commandLocator->getArguments());
+ $this->assertSame([], $container->getParameter('console.command.ids'));
+
+ $initCounter = DescribedCommand::$initCounter;
+ $command = $container->get('console.command_loader')->get('cmdname');
+
+ $this->assertInstanceOf(LazyCommand::class, $command);
+ $this->assertSame(['cmdalias'], $command->getAliases());
+ $this->assertSame('Just testing', $command->getDescription());
+ $this->assertTrue($command->isHidden());
+ $this->assertTrue($command->isEnabled());
+ $this->assertSame($initCounter, DescribedCommand::$initCounter);
+
+ $this->assertSame('', $command->getHelp());
+ $this->assertSame(1 + $initCounter, DescribedCommand::$initCounter);
+ }
+
public function testProcessThrowAnExceptionIfTheServiceIsAbstract()
{
$this->expectException(\InvalidArgumentException::class);
@@ -250,3 +285,18 @@ class NamedCommand extends Command
{
protected static $defaultName = 'default';
}
+
+class DescribedCommand extends Command
+{
+ public static $initCounter = 0;
+
+ protected static $defaultName = '|cmdname|cmdalias';
+ protected static $defaultDescription = 'Just testing';
+
+ public function __construct()
+ {
+ ++self::$initCounter;
+
+ parent::__construct();
+ }
+}
diff --git a/src/Symfony/Component/Form/Command/DebugCommand.php b/src/Symfony/Component/Form/Command/DebugCommand.php
index 4150feaf8ce85..9eac585dc8548 100644
--- a/src/Symfony/Component/Form/Command/DebugCommand.php
+++ b/src/Symfony/Component/Form/Command/DebugCommand.php
@@ -32,6 +32,7 @@
class DebugCommand extends Command
{
protected static $defaultName = 'debug:form';
+ protected static $defaultDescription = 'Displays form type information';
private $formRegistry;
private $namespaces;
@@ -64,7 +65,7 @@ protected function configure()
new InputOption('show-deprecated', null, InputOption::VALUE_NONE, 'Display deprecated options in form types'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt or json)', 'txt'),
])
- ->setDescription('Displays form type information')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% command displays information about form types.
diff --git a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php
index 03320b6f66e15..b289a29c0ef40 100644
--- a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php
+++ b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php
@@ -36,6 +36,7 @@
class ConsumeMessagesCommand extends Command
{
protected static $defaultName = 'messenger:consume';
+ protected static $defaultDescription = 'Consumes messages';
private $routableBus;
private $receiverLocator;
@@ -71,7 +72,7 @@ protected function configure(): void
new InputOption('sleep', null, InputOption::VALUE_REQUIRED, 'Seconds to sleep before asking for new messages after no messages were found', 1),
new InputOption('bus', 'b', InputOption::VALUE_REQUIRED, 'Name of the bus to which received messages should be dispatched (if not passed, bus is determined automatically)'),
])
- ->setDescription('Consumes messages')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% command consumes messages and dispatches them to the message bus.
diff --git a/src/Symfony/Component/Messenger/Command/DebugCommand.php b/src/Symfony/Component/Messenger/Command/DebugCommand.php
index 31b19d0bff948..047ceaf6ee18d 100644
--- a/src/Symfony/Component/Messenger/Command/DebugCommand.php
+++ b/src/Symfony/Component/Messenger/Command/DebugCommand.php
@@ -26,6 +26,7 @@
class DebugCommand extends Command
{
protected static $defaultName = 'debug:messenger';
+ protected static $defaultDescription = 'Lists messages you can dispatch using the message buses';
private $mapping;
@@ -43,7 +44,7 @@ protected function configure()
{
$this
->addArgument('bus', InputArgument::OPTIONAL, sprintf('The bus id (one of "%s")', implode('", "', array_keys($this->mapping))))
- ->setDescription('Lists messages you can dispatch using the message buses')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% command displays all messages that can be
dispatched using the message buses:
diff --git a/src/Symfony/Component/Messenger/Command/FailedMessagesRemoveCommand.php b/src/Symfony/Component/Messenger/Command/FailedMessagesRemoveCommand.php
index 951b7d499ed1b..df21b9bcd4f5a 100644
--- a/src/Symfony/Component/Messenger/Command/FailedMessagesRemoveCommand.php
+++ b/src/Symfony/Component/Messenger/Command/FailedMessagesRemoveCommand.php
@@ -27,6 +27,7 @@
class FailedMessagesRemoveCommand extends AbstractFailedMessagesCommand
{
protected static $defaultName = 'messenger:failed:remove';
+ protected static $defaultDescription = 'Remove given messages from the failure transport';
/**
* {@inheritdoc}
@@ -39,7 +40,7 @@ protected function configure(): void
new InputOption('force', null, InputOption::VALUE_NONE, 'Force the operation without confirmation'),
new InputOption('show-messages', null, InputOption::VALUE_NONE, 'Display messages before removing it (if multiple ids are given)'),
])
- ->setDescription('Remove given messages from the failure transport')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% removes given messages that are pending in the failure transport.
diff --git a/src/Symfony/Component/Messenger/Command/FailedMessagesRetryCommand.php b/src/Symfony/Component/Messenger/Command/FailedMessagesRetryCommand.php
index 87426edd9dbaa..f56eeb3345b4d 100644
--- a/src/Symfony/Component/Messenger/Command/FailedMessagesRetryCommand.php
+++ b/src/Symfony/Component/Messenger/Command/FailedMessagesRetryCommand.php
@@ -35,6 +35,7 @@
class FailedMessagesRetryCommand extends AbstractFailedMessagesCommand
{
protected static $defaultName = 'messenger:failed:retry';
+ protected static $defaultDescription = 'Retries one or more messages from the failure transport';
private $eventDispatcher;
private $messageBus;
@@ -59,7 +60,7 @@ protected function configure(): void
new InputArgument('id', InputArgument::IS_ARRAY, 'Specific message id(s) to retry'),
new InputOption('force', null, InputOption::VALUE_NONE, 'Force action without confirmation'),
])
- ->setDescription('Retries one or more messages from the failure transport')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% retries message in the failure transport.
diff --git a/src/Symfony/Component/Messenger/Command/FailedMessagesShowCommand.php b/src/Symfony/Component/Messenger/Command/FailedMessagesShowCommand.php
index bf8a72f906367..4a97159cf7539 100644
--- a/src/Symfony/Component/Messenger/Command/FailedMessagesShowCommand.php
+++ b/src/Symfony/Component/Messenger/Command/FailedMessagesShowCommand.php
@@ -28,6 +28,7 @@
class FailedMessagesShowCommand extends AbstractFailedMessagesCommand
{
protected static $defaultName = 'messenger:failed:show';
+ protected static $defaultDescription = 'Shows one or more messages from the failure transport';
/**
* {@inheritdoc}
@@ -39,7 +40,7 @@ protected function configure(): void
new InputArgument('id', InputArgument::OPTIONAL, 'Specific message id to show'),
new InputOption('max', null, InputOption::VALUE_REQUIRED, 'Maximum number of messages to list', 50),
])
- ->setDescription('Shows one or more messages from the failure transport')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% shows message that are pending in the failure transport.
diff --git a/src/Symfony/Component/Messenger/Command/SetupTransportsCommand.php b/src/Symfony/Component/Messenger/Command/SetupTransportsCommand.php
index 84395892fdf9b..71dd7ab5fd1a8 100644
--- a/src/Symfony/Component/Messenger/Command/SetupTransportsCommand.php
+++ b/src/Symfony/Component/Messenger/Command/SetupTransportsCommand.php
@@ -25,6 +25,7 @@
class SetupTransportsCommand extends Command
{
protected static $defaultName = 'messenger:setup-transports';
+ protected static $defaultDescription = 'Prepares the required infrastructure for the transport';
private $transportLocator;
private $transportNames;
@@ -41,7 +42,7 @@ protected function configure()
{
$this
->addArgument('transport', InputArgument::OPTIONAL, 'Name of the transport to setup', null)
- ->setDescription('Prepares the required infrastructure for the transport')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<%command.name% command setups the transports:
diff --git a/src/Symfony/Component/Messenger/Command/StopWorkersCommand.php b/src/Symfony/Component/Messenger/Command/StopWorkersCommand.php
index 7df9b9d2014f0..c861513402b6a 100644
--- a/src/Symfony/Component/Messenger/Command/StopWorkersCommand.php
+++ b/src/Symfony/Component/Messenger/Command/StopWorkersCommand.php
@@ -25,6 +25,7 @@
class StopWorkersCommand extends Command
{
protected static $defaultName = 'messenger:stop-workers';
+ protected static $defaultDescription = 'Stops workers after their current message';
private $restartSignalCachePool;
@@ -42,7 +43,7 @@ protected function configure(): void
{
$this
->setDefinition([])
- ->setDescription('Stops workers after their current message')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% command sends a signal to stop any messenger:consume processes that are running.
diff --git a/src/Symfony/Component/Translation/Command/XliffLintCommand.php b/src/Symfony/Component/Translation/Command/XliffLintCommand.php
index dc2e9a9d936dd..f246b7ea33491 100644
--- a/src/Symfony/Component/Translation/Command/XliffLintCommand.php
+++ b/src/Symfony/Component/Translation/Command/XliffLintCommand.php
@@ -31,6 +31,7 @@
class XliffLintCommand extends Command
{
protected static $defaultName = 'lint:xliff';
+ protected static $defaultDescription = 'Lints a XLIFF file and outputs encountered errors';
private $format;
private $displayCorrectFiles;
@@ -53,7 +54,7 @@ public function __construct(string $name = null, callable $directoryIteratorProv
protected function configure()
{
$this
- ->setDescription('Lints a XLIFF file and outputs encountered errors')
+ ->setDescription(self::$defaultDescription)
->addArgument('filename', InputArgument::IS_ARRAY, 'A file, a directory or "-" for reading from STDIN')
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
->setHelp(<<addArgument('class', InputArgument::REQUIRED, 'A fully qualified class name or a path')
->addOption('show-all', null, InputOption::VALUE_NONE, 'Show all classes even if they have no validation constraints')
- ->setDescription('Displays validation constraints for classes')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
The %command.name% 'App\Entity\Dummy' command dumps the validators for the dummy class.
diff --git a/src/Symfony/Component/VarDumper/Command/ServerDumpCommand.php b/src/Symfony/Component/VarDumper/Command/ServerDumpCommand.php
index c8a61da98c5b6..8b50bc3c7f16e 100644
--- a/src/Symfony/Component/VarDumper/Command/ServerDumpCommand.php
+++ b/src/Symfony/Component/VarDumper/Command/ServerDumpCommand.php
@@ -35,6 +35,7 @@
class ServerDumpCommand extends Command
{
protected static $defaultName = 'server:dump';
+ protected static $defaultDescription = 'Starts a dump server that collects and displays dumps in a single place';
private $server;
@@ -58,7 +59,7 @@ protected function configure()
$this
->addOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format (%s)', $availableFormats), 'cli')
- ->setDescription('Starts a dump server that collects and displays dumps in a single place')
+ ->setDescription(self::$defaultDescription)
->setHelp(<<<'EOF'
%command.name% starts a dump server that collects and displays
dumps in a single place for debugging you application:
diff --git a/src/Symfony/Component/Yaml/Command/LintCommand.php b/src/Symfony/Component/Yaml/Command/LintCommand.php
index 94a84b754d213..5375bf460d3c1 100644
--- a/src/Symfony/Component/Yaml/Command/LintCommand.php
+++ b/src/Symfony/Component/Yaml/Command/LintCommand.php
@@ -33,6 +33,7 @@
class LintCommand extends Command
{
protected static $defaultName = 'lint:yaml';
+ protected static $defaultDescription = 'Lints a file and outputs encountered errors';
private $parser;
private $format;
@@ -54,7 +55,7 @@ public function __construct(string $name = null, callable $directoryIteratorProv
protected function configure()
{
$this
- ->setDescription('Lints a file and outputs encountered errors')
+ ->setDescription(self::$defaultDescription)
->addArgument('filename', InputArgument::IS_ARRAY, 'A file, a directory or "-" for reading from STDIN')
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format')
->addOption('parse-tags', null, InputOption::VALUE_NONE, 'Parse custom tags')