Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e348b70

Browse files
committed
feature #59473 [Console] Add broader support for command "help" definition (yceruto)
This PR was squashed before being merged into the 7.3 branch. Discussion ---------- [Console] Add broader support for command "help" definition | Q | A | ------------- | --- | Branch? | 7.3 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | - | License | MIT Follow up #59340 Invokable and regular commands can now define the command `help` content via the `#[AsCommand]` attribute. This is particularly useful for invokable commands, as it avoids the need to extend the `Command` class. ```php #[AsCommand( name: 'user:create', description: 'Create a new user', help: <<<TXT The <info>%command.name%</info> command generates a new user class for security and updates your security.yaml file for it. It will also generate a user provider class if your situation needs a custom class. <info>php %command.full_name% email</info> If the argument is missing, the command will ask for the class name interactively. TXT )] class CreateUserCommand { public function __invoke(SymfonyStyle $io, #[Argument] string $email): int { // ... } } ``` Cheers! Commits ------- e9a6b0a [Console] Add broader support for command "help" definition
2 parents da818b7 + e9a6b0a commit e348b70

File tree

7 files changed

+33
-13
lines changed

7 files changed

+33
-13
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

+5-1
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,11 @@ public function load(array $configs, ContainerBuilder $container): void
611611
$container->registerForAutoconfiguration(AssetCompilerInterface::class)
612612
->addTag('asset_mapper.compiler');
613613
$container->registerAttributeForAutoconfiguration(AsCommand::class, static function (ChildDefinition $definition, AsCommand $attribute, \ReflectionClass $reflector): void {
614-
$definition->addTag('console.command', ['command' => $attribute->name, 'description' => $attribute->description]);
614+
$definition->addTag('console.command', [
615+
'command' => $attribute->name,
616+
'description' => $attribute->description,
617+
'help' => $attribute->help,
618+
]);
615619
});
616620
$container->registerForAutoconfiguration(Command::class)
617621
->addTag('console.command');

src/Symfony/Component/Console/Attribute/AsCommand.php

+2
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@ class AsCommand
2222
* @param string|null $description The description of the command, displayed with the help page
2323
* @param string[] $aliases The list of aliases of the command. The command will be executed when using one of them (i.e. "cache:clean")
2424
* @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
25+
* @param string|null $help The help content of the command, displayed with the help page
2526
*/
2627
public function __construct(
2728
public string $name,
2829
public ?string $description = null,
2930
array $aliases = [],
3031
bool $hidden = false,
32+
public ?string $help = null,
3133
) {
3234
if (!$hidden && !$aliases) {
3335
return;

src/Symfony/Component/Console/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CHANGELOG
66

77
* Add support for invokable commands and add `#[Argument]` and `#[Option]` attributes to define input arguments and options
88
* Deprecate not declaring the parameter type in callable commands defined through `setCode` method
9+
* Add support for help definition via `AsCommand` attribute
910

1011
7.2
1112
---

src/Symfony/Component/Console/Command/Command.php

+4
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ public function __construct(?string $name = null)
100100
$this->setDescription(static::getDefaultDescription() ?? '');
101101
}
102102

103+
if ('' === $this->help && $attributes = (new \ReflectionClass(static::class))->getAttributes(AsCommand::class)) {
104+
$this->setHelp($attributes[0]->newInstance()->help ?? '');
105+
}
106+
103107
if (\is_callable($this)) {
104108
$this->code = new InvokableCommand($this, $this(...));
105109
}

src/Symfony/Component/Console/DependencyInjection/AddConsoleCommandPass.php

+8-8
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ public function process(ContainerBuilder $container): void
5353
$invokableRef = new Reference($id);
5454
$definition = $container->register($id .= '.command', $class = Command::class)
5555
->addMethodCall('setCode', [$invokableRef]);
56+
} else {
57+
$invokableRef = null;
5658
}
5759

5860
$aliases = $tags[0]['command'] ?? str_replace('%', '%%', $class::getDefaultName() ?? '');
@@ -75,6 +77,7 @@ public function process(ContainerBuilder $container): void
7577
}
7678

7779
$description = $tags[0]['description'] ?? null;
80+
$help = $tags[0]['help'] ?? null;
7881

7982
unset($tags[0]);
8083
$lazyCommandMap[$commandName] = $id;
@@ -91,6 +94,7 @@ public function process(ContainerBuilder $container): void
9194
}
9295

9396
$description ??= $tag['description'] ?? null;
97+
$help ??= $tag['help'] ?? null;
9498
}
9599

96100
$definition->addMethodCall('setName', [$commandName]);
@@ -103,16 +107,12 @@ public function process(ContainerBuilder $container): void
103107
$definition->addMethodCall('setHidden', [true]);
104108
}
105109

106-
if (!$description) {
107-
if (!$r = $container->getReflectionClass($class)) {
108-
throw new InvalidArgumentException(\sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
109-
}
110-
if (!$r->isSubclassOf(Command::class)) {
111-
throw new InvalidArgumentException(\sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, 'console.command', Command::class));
112-
}
113-
$description = str_replace('%', '%%', $class::getDefaultDescription() ?? '');
110+
if ($help && $invokableRef) {
111+
$definition->addMethodCall('setHelp', [str_replace('%', '%%', $help)]);
114112
}
115113

114+
$description ??= str_replace('%', '%%', $class::getDefaultDescription() ?? '');
115+
116116
if ($description) {
117117
$definition->addMethodCall('setDescription', [$description]);
118118

src/Symfony/Component/Console/Tests/Command/CommandTest.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@ public function testCommandAttribute()
434434

435435
$this->assertSame('foo', $command->getName());
436436
$this->assertSame('desc', $command->getDescription());
437+
$this->assertSame('help', $command->getHelp());
437438
$this->assertTrue($command->isHidden());
438439
$this->assertSame(['f'], $command->getAliases());
439440
}
@@ -473,7 +474,7 @@ function createClosure()
473474
};
474475
}
475476

476-
#[AsCommand(name: 'foo', description: 'desc', hidden: true, aliases: ['f'])]
477+
#[AsCommand(name: 'foo', description: 'desc', hidden: true, aliases: ['f'], help: 'help')]
477478
class Php8Command extends Command
478479
{
479480
}

src/Symfony/Component/Console/Tests/DependencyInjection/AddConsoleCommandPassTest.php

+11-3
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ public function testEscapesDefaultFromPhp()
176176
$this->assertSame('%cmd%', $command->getName());
177177
$this->assertSame(['%cmdalias%'], $command->getAliases());
178178
$this->assertSame('Creates a 80% discount', $command->getDescription());
179+
$this->assertSame('The %command.name% help content.', $command->getHelp());
179180
}
180181

181182
public function testProcessThrowAnExceptionIfTheServiceIsAbstract()
@@ -310,12 +311,19 @@ public function testProcessInvokableCommand()
310311
$container->addCompilerPass(new AddConsoleCommandPass(), PassConfig::TYPE_BEFORE_REMOVING);
311312

312313
$definition = new Definition(InvokableCommand::class);
313-
$definition->addTag('console.command', ['command' => 'invokable', 'description' => 'Just testing']);
314+
$definition->addTag('console.command', [
315+
'command' => 'invokable',
316+
'description' => 'The command description',
317+
'help' => 'The %command.name% command help content.',
318+
]);
314319
$container->setDefinition('invokable_command', $definition);
315320

316321
$container->compile();
322+
$command = $container->get('console.command_loader')->get('invokable');
317323

318324
self::assertTrue($container->has('invokable_command.command'));
325+
self::assertSame('The command description', $command->getDescription());
326+
self::assertSame('The %command.name% command help content.', $command->getHelp());
319327
}
320328
}
321329

@@ -328,7 +336,7 @@ class NamedCommand extends Command
328336
{
329337
}
330338

331-
#[AsCommand(name: '%cmd%|%cmdalias%', description: 'Creates a 80% discount')]
339+
#[AsCommand(name: '%cmd%|%cmdalias%', description: 'Creates a 80% discount', help: 'The %command.name% help content.')]
332340
class EscapedDefaultsFromPhpCommand extends Command
333341
{
334342
}
@@ -346,7 +354,7 @@ public function __construct()
346354
}
347355
}
348356

349-
#[AsCommand(name: 'invokable', description: 'Just testing')]
357+
#[AsCommand(name: 'invokable', description: 'Just testing', help: 'The %command.name% help content.')]
350358
class InvokableCommand
351359
{
352360
public function __invoke(): void

0 commit comments

Comments
 (0)