Skip to content

Commit e9a6b0a

Browse files
ycerutochalasr
authored andcommitted
[Console] Add broader support for command "help" definition
1 parent da818b7 commit e9a6b0a

File tree

7 files changed

+33
-13
lines changed

7 files changed

+33
-13
lines changed

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

Lines changed: 5 additions & 1 deletion
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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 4 additions & 0 deletions
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

Lines changed: 8 additions & 8 deletions
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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 11 additions & 3 deletions
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)