diff --git a/src/Symfony/Component/Console/Attribute/AsCommand.php b/src/Symfony/Component/Console/Attribute/AsCommand.php index 6066d7c533d54..4f98a712212f2 100644 --- a/src/Symfony/Component/Console/Attribute/AsCommand.php +++ b/src/Symfony/Component/Console/Attribute/AsCommand.php @@ -11,6 +11,10 @@ namespace Symfony\Component\Console\Attribute; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + /** * Service tag to autoconfigure commands. */ @@ -18,16 +22,18 @@ class AsCommand { /** - * @param string $name The name of the command, used when calling it (i.e. "cache:clear") - * @param string|null $description The description of the command, displayed with the help page - * @param string[] $aliases The list of aliases of the command. The command will be executed when using one of them (i.e. "cache:clean") - * @param bool $hidden If true, the command won't be shown when listing all the available commands, but it can still be run as any other command + * @param string $name The name of the command, used when calling it (i.e. "cache:clear") + * @param string|null $description The description of the command, displayed with the help page + * @param string[] $aliases The list of aliases of the command. The command will be executed when using one of them (i.e. "cache:clean") + * @param bool $hidden If true, the command won't be shown when listing all the available commands, but it can still be run as any other command + * @param array|InputDefinition $inputDefinition The list of argument and option instances */ public function __construct( public string $name, public ?string $description = null, array $aliases = [], bool $hidden = false, + public array|InputDefinition $inputDefinition = [], ) { if (!$hidden && !$aliases) { return; diff --git a/src/Symfony/Component/Console/CHANGELOG.md b/src/Symfony/Component/Console/CHANGELOG.md index 25d7f7179723c..877ebbb2d176d 100644 --- a/src/Symfony/Component/Console/CHANGELOG.md +++ b/src/Symfony/Component/Console/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.2 +--- + + * Add the `inputDefinition` option to configure arguments and options in the `AsCommand` attribute + 7.1 --- diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index 03da6db43f335..376cf641b67c4 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -100,6 +100,10 @@ public function __construct(?string $name = null) $this->setDescription(static::getDefaultDescription() ?? ''); } + if ($attribute = (new \ReflectionClass(static::class))->getAttributes(AsCommand::class)) { + $this->setDefinition($attribute[0]->newInstance()->inputDefinition); + } + $this->configure(); } diff --git a/src/Symfony/Component/Console/Tests/Command/CommandTest.php b/src/Symfony/Component/Console/Tests/Command/CommandTest.php index f3e4b51d16991..8e51e0fb6330d 100644 --- a/src/Symfony/Component/Console/Tests/Command/CommandTest.php +++ b/src/Symfony/Component/Console/Tests/Command/CommandTest.php @@ -462,6 +462,30 @@ public function testDefaultCommand() $this->assertEquals('foo2', $property->getValue($apl)); } + + public function testCommandWithDefinitionAttribute() + { + $this->assertSame('my:command:with-definition', CommandWithDefinition::getDefaultName()); + + $command = new CommandWithDefinition(); + + $this->assertSame('my:command:with-definition', $command->getName()); + + $this->assertTrue($command->getDefinition()->hasArgument('target')); + $this->assertTrue($command->getDefinition()->hasOption('symlink')); + } + + public function testCommandWithDefinitionObjectAttribute() + { + $this->assertSame('my:command:with-definition-object', CommandWithDefinitionObject::getDefaultName()); + + $command = new CommandWithDefinitionObject(); + + $this->assertSame('my:command:with-definition-object', $command->getName()); + + $this->assertTrue($command->getDefinition()->hasArgument('target')); + $this->assertTrue($command->getDefinition()->hasOption('symlink')); + } } // In order to get an unbound closure, we should create it outside a class @@ -490,3 +514,24 @@ class MyAnnotatedCommand extends Command protected static $defaultDescription = 'This description should be ignored.'; } + +#[AsCommand( + name: 'my:command:with-definition', + inputDefinition: [ + new InputArgument('target', InputArgument::OPTIONAL, 'The target directory', null), + new InputOption('symlink', null, InputOption::VALUE_NONE), + ] +)] +class CommandWithDefinition extends Command +{ +} +#[AsCommand( + name: 'my:command:with-definition-object', + inputDefinition: new InputDefinition([ + new InputArgument('target', InputArgument::OPTIONAL, 'The target directory', null), + new InputOption('symlink', null, InputOption::VALUE_NONE), + ]) +)] +class CommandWithDefinitionObject extends Command +{ +}