8000 Command simplification and deprecations · symfony/symfony@f61c319 · GitHub
[go: up one dir, main page]

Skip to content

Commit f61c319

Browse files
committed
Command simplification and deprecations
1 parent e348b70 commit f61c319

File tree

6 files changed

+112
-43
lines changed

6 files changed

+112
-43
lines changed

UPGRADE-7.3.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ Console
3030
});
3131
```
3232

33+
* Static methods `Command::getDefaultName()` and `Command::getDefaultDescription()` are deprecated.
34+
Extract the command name and description through class reflection instead
35+
3336
FrameworkBundle
3437
---------------
3538

src/Symfony/Component/Console/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ CHANGELOG
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
99
* Add support for help definition via `AsCommand` attribute
10+
* Delay command initialization and configuration
11+
* Deprecate static methods `Command::getDefaultName()` and `Command::getDefaultDescription()`
1012

1113
7.2
1214
---

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

Lines changed: 76 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,29 @@ class Command
5353
private array $synopsis = [];
5454
private array $usages = [];
5555
private ?HelperSet $helperSet = null;
56+
private bool $initialized = false;
5657

58+
/**
59+
* @deprecated since Symfony 7.3
60+
*/
5761
public static function getDefaultName(): ?string
5862
{
63+
trigger_deprecation('symfony/console', '7.3', 'The static method "%s()" is deprecated and will be removed in Symfony 8.0, extract the command name from the "%s" attribute instead.', __METHOD__, AsCommand::class);
64+
5965
if ($attribute = (new \ReflectionClass(static::class))->getAttributes(AsCommand::class)) {
6066
return $attribute[0]->newInstance()->name;
6167
}
6268

6369
return null;
6470
}
6571

72+
/**
73+
* @deprecated since Symfony 7.3
74+
*/
6675
public static function getDefaultDescription(): ?string
6776
{
77+
trigger_deprecation('symfony/console', '7.3', 'The static method "%s()" is deprecated and will be removed in Symfony 8.0, extract the command description from the "%s" attribute instead.', __METHOD__, AsCommand::class);
78+
6879
if ($attribute = (new \ReflectionClass(static::class))->getAttributes(AsCommand::class)) {
6980
return $attribute[0]->newInstance()->description;
7081
}
@@ -79,36 +90,7 @@ public static function getDefaultDescription(): ?string
7990
*/
8091
public function __construct(?string $name = null)
8192
{
82-
$this->definition = new InputDefinition();
83-
84-
if (null === $name && null !== $name = static::getDefaultName()) {
85-
$aliases = explode('|', $name);
86-
87-
if ('' === $name = array_shift($aliases)) {
88-
$this->setHidden(true);
89-
$name = array_shift($aliases);
90-
}
91-
92-
$this->setAliases($aliases);
93-
}
94-
95-
if (null !== $name) {
96-
$this->setName($name);
97-
}
98-
99-
if ('' === $this->description) {
100-
$this->setDescription(static::getDefaultDescription() ?? '');
101-
}
102-
103-
if ('' === $this->help && $attributes = (new \ReflectionClass(static::class))->getAttributes(AsCommand::class)) {
104-
$this->setHelp($attributes[0]->newInstance()->help ?? '');
105-
}
106-
107-
if (\is_callable($this)) {
108-
$this->code = new InvokableCommand($this, $this(...));
109-
}
110-
111-
$this->configure();
93+
$this->init($name);
11294
}
11395

11496
/**
@@ -333,6 +315,8 @@ public function setCode(callable $code): static
333315
*/
334316
public function mergeApplicationDefinition(bool $mergeArgs = true): void
335317
{
318+
$this->init();
319+
336320
if (null === $this->application) {
337321
return;
338322
}
@@ -356,6 +340,8 @@ public function mergeApplicationDefinition(bool $mergeArgs = true): void
356340
*/
357341
public function setDefinition(array|InputDefinition $definition): static
358342
{
343+
$this->init();
344+
359345
if ($definition instanceof InputDefinition) {
360346
$this->definition = $definition;
361347
} else {
@@ -385,6 +371,8 @@ public function getDefinition(): InputDefinition
385371
*/
386372
public function getNativeDefinition(): InputDefinition
387373
{
374+
$this->init();
375+
388376
$definition = $this->definition ?? throw new LogicException(\sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', static::class));
389377

390378
if ($this->code && !$definition->getArguments() && !$definition->getOptions()) {
@@ -407,6 +395,8 @@ public function getNativeDefinition(): InputDefinition
407395
*/
408396
public function addArgument(string $name, ?int $mode = null, string $description = '', mixed $default = null, array|\Closure $suggestedValues = []): static
409397
{
398+
$this->init();
399+
410400
$this->definition->addArgument(new InputArgument($name, $mode, $description, $default, $suggestedValues));
411401
$this->fullDefinition?->addArgument(new InputArgument($name, $mode, $description, $default, $suggestedValues));
412402

@@ -427,6 +417,8 @@ public function addArgument(string $name, ?int $mode = null, string $description
427417
*/
428418
public function addOption(string $name, string|array|null $shortcut = null, ?int $mode = null, string $description = '', mixed $default = null, array|\Closure $suggestedValues = []): static
429419
{
420+
$this->init();
421+
430422
$this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default, $suggestedValues));
431423
$this->fullDefinition?->addOption(new InputOption($name, $shortcut, $mode, $description, $default, $suggestedValues));
432424

@@ -474,6 +466,8 @@ public function setProcessTitle(string $title): static
474466
*/
475467
public function getName(): ?string
476468
{
469+
$this->init();
470+
477471
return $this->name;
478472
}
479473

@@ -494,6 +488,8 @@ public function setHidden(bool $hidden = true): static
494488
*/
495489
public function isHidden(): bool
496490
{
491+
$this->init();
492+
497493
return $this->hidden;
498494
}
499495

@@ -514,6 +510,8 @@ public function setDescription(string $description): static
514510
*/
515511
public function getDescription(): string
516512
{
513+
$this->init();
514+
517515
return $this->description;
518516
}
519517

@@ -534,6 +532,8 @@ public function setHelp(string $help): static
534532
*/
535533
public function getHelp(): string
536534
{
535+
$this->init();
536+
537537
return $this->help;
538538
}
539539

< 1241 div aria-hidden="true" style="left:-2px" class="position-absolute top-0 d-flex user-select-none DiffLineTableCellParts-module__in-progress-comment-indicator--hx3m3">
@@ -586,6 +586,8 @@ public function setAliases(iterable $aliases): static
586586
*/
587587
public function getAliases(): array
588588
{
589+
$this->init();
590+
589591
return $this->aliases;
590592
}
591593

@@ -596,6 +598,8 @@ public function getAliases(): array
596598
*/
597599
public function getSynopsis(bool $short = false): string
598600
{
601+
$this->init();
602+
599603
$key = $short ? 'short' : 'long';
600604

601605
if (!isset($this->synopsis[$key])) {
@@ -644,6 +648,48 @@ public function getHelper(string $name): HelperInterface
644648
return $this->helperSet->get($name);
645649
}
646650

651+
private function init(?string $name = null): void
652+
{
653+
if ($this->initialized) {
654+
return;
655+
}
656+
657+
$this->definition = new InputDefinition();
658+
659+
$attribute = ((new \ReflectionClass(static::class))->getAttributes(AsCommand::class)[0] ?? null)?->newInstance();
660+
661+
if (null === $name && null !== $name = $attribute?->name) {
662+
$aliases = explode('|', $name);
663+
664+
if ('' === $name = array_shift($aliases)) {
665+
$this->setHidden(true);
666+
$name = array_shift($aliases);
667+
}
668+
669+
$this->setAliases($aliases);
670+
}
671+
672+
if (null !== $name) {
673+
$this->setName($name);
674+
}
675+
676+
if ('' === $this->description && $attribute?->description) {
677+
$this->setDescription($attribute->description);
678+
}
679+
680+
if ('' === $this->help && $attribute?->help) {
681+
$this->setHelp($attribute->help);
682+
}
683+
684+
if (\is_callable($this)) {
685+
$this->code = new InvokableCommand($this, $this(...));
686+
}
687+
688+
$this->initialized = true;
689+
690+
$this->configure();
691+
}
692+
647693
/**
648694
* Validates a command name.
649695
*

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Console\DependencyInjection;
1313

14+
use Symfony\Component\Console\Attribute\AsCommand;
1415
use Symfony\Component\Console\Command\Command;
1516
use Symfony\Component\Console\Command\LazyCommand;
1617
use Symfony\Component\Console\CommandLoader\ContainerCommandLoader;
@@ -57,7 +58,10 @@ public function process(ContainerBuilder $container): void
5758
$invokableRef = null;
5859
}
5960

60-
$aliases = $tags[0]['command'] ?? str_replace('%', '%%', $class::getDefaultName() ?? '');
61+
/** @var AsCommand|null $attribute */
62+
$attribute = ($r->getAttributes(AsCommand::class)[0] ?? null)?->newInstance();
63+
64+
$aliases = str_replace('%', '%%', $tags[0]['command'] ?? $attribute?->name ?? '');
6165
$aliases = explode('|', $aliases);
6266
$commandName = array_shift($aliases);
6367

@@ -111,10 +115,10 @@ public function process(ContainerBuilder $container): void
111115
$definition->addMethodCall('setHelp', [str_replace('%', '%%', $help)]);
112116
}
113117

114-
$description ??= str_replace('%', '%%', $class::getDefaultDescription() ?? '');
118+
$description ??= $attribute?->description ?? '';
115119

116120
if ($description) {
117-
$definition->addMethodCall('setDescription', [$description]);
121+
$definition->addMethodCall('setDescription', [str_replace('%', '%%', $description)]);
118122

119123
$container->register('.'.$id.'.lazy', LazyCommand::class)
120124
->setArguments([$commandName, $aliases, $description, $isHidden, new ServiceClosureArgument($lazyCommandRefs[$id])]);

src/Symfony/Component/Console/Tests/ApplicationTest.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ public function testAdd()
232232
public function testAddCommandWithEmptyConstructor()
233233
{
234234
$this->expectException(\LogicException::class);
235-
$this->expectExceptionMessage('Command class "Foo5Command" is not correctly initialized. You probably forgot to call the parent constructor.');
235+
$this->expectExceptionMessage('The command defined in "Foo5Command" cannot have an empty name.');
236236

237237
(new Application())->add(new \Foo5Command());
238238
}
@@ -2404,7 +2404,9 @@ private function createSignalableApplication(Command $command, ?EventDispatcherI
24042404
if ($dispatcher) {
24052405
$application->setDispatcher($dispatcher);
24062406
}
2407-
$application->add(new LazyCommand($command::getDefaultName(), [], '', false, fn () => $command, true));
2407+
/** @var AsCommand $attribute */
2408+
$attribute = ((new \ReflectionClass($command))->getAttributes(AsCommand::class)[0] ?? null)?->newInstance();
2409+
$application->add(new LazyCommand($attribute->name, [], '', false, fn () => $command, true));
24082410

24092411
return $application;
24102412
}

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

Lines changed: 20 additions & 8 deletions
C2EE
Original file line numberDiff line numberDiff line change
@@ -427,9 +427,6 @@ public function testSetCodeWithStaticAnonymousFunction()
427427

428428
public function testCommandAttribute()
429429
{
430-
$this->assertSame('|foo|f', Php8Command::getDefaultName());
431-
$this->assertSame('desc', Php8Command::getDefaultDescription());
432-
433430
$command = new Php8Command();
434431

435432
$this->assertSame('foo', $command->getName());
@@ -439,26 +436,41 @@ public function testCommandAttribute()
439436
$this->assertSame(['f'], $command->getAliases());
440437
}
441438

442-
public function testAttributeOverridesProperty()
439+
/**
440+
* @group legacy
441+
*/
442+
public function testCommandAttributeWithDeprecatedMethods()
443443
{
444-
$this->assertSame('my:command', MyAnnotatedCommand::getDefaultName());
445-
$this->assertSame('This is a command I wrote all by myself', MyAnnotatedCommand::getDefaultDescription());
444+
$this->assertSame('|foo|f', Php8Command::getDefaultName());
445+
$this->assertSame('desc', Php8Command::getDefaultDescription());
446+
}
446447

448+
public function testAttributeOverridesProperty()
449+
{
447450
$command = new MyAnnotatedCommand();
448451

449452
$this->assertSame('my:command', $command->getName());
450453
$this->assertSame('This is a command I wrote all by myself', $command->getDescription());
451454
}
452455

456+
/**
457+
* @group legacy
458+
*/
459+
public function testAttributeOverridesPropertyWithDeprecatedMethods()
460+
{
461+
$this->assertSame('my:command', MyAnnotatedCommand::getDefaultName());
462+
$this->assertSame('This is a command I wrote all by myself', MyAnnotatedCommand::getDefaultDescription());
463+
}
464+
453465
public function testDefaultCommand()
454466
{
455467
$apl = new Application();
456-
$apl->setDefaultCommand(Php8Command::getDefaultName());
468+
$apl->setDefaultCommand('foo');
457469
$property = new \ReflectionProperty($apl, 'defaultCommand');
458470

459471
$this->assertEquals('foo', $property->getValue($apl));
460472

461-
$apl->setDefaultCommand(Php8Command2::getDefaultName());
473+
$apl->setDefaultCommand('foo2');
462474
$property = new \ReflectionProperty($apl, 'defaultCommand');
463475

464476
$this->assertEquals('foo2', $property->getValue($apl));

0 commit comments

Comments
 (0)
0