Skip to content

Commit f10093d

Browse files
committed
feature symfony#61078 [Console] Add getter for the original command "code" object (weitzman)
This PR was squashed before being merged into the 7.4 branch. Discussion ---------- [Console] Add getter for the original command "code" object | Q | A | ------------- | --- | Branch? | 7.4 | Bug fix? | no | New feature? | yes | Deprecations? | no | License | MIT I'm playing with Invokable Commands and am seeing a limitation which is bothersome. Attributes attached to the invokable class are lost by the time we have a `Command` added to the Application. Consider an invokable class like below. The #[CLI\FieldLabels] and #[CLI\DefaultFields] Attributes are not retrievable from the Command. That is, there is no easy way to access the actual InvokableCommand object instead of the [wrapping Command](https://github.com/symfony/symfony/blob/a384c231a0051c0ac10e89c2a0516343f9fd3187/src/Symfony/Component/Console/Application.php#L553). With this PR, Attributes become accessible via `$command->getCode()->getCallable()` ```php namespace Drush\Commands\core; #[AsCommand( name: 'twig:unused', description: 'Find potentially unused Twig templates.', aliases: ['twu'], )] #[CLI\FieldLabels(labels: ['template' => 'Template', 'compiled' => 'Compiled'])] #[CLI\DefaultFields(fields: ['template', 'compiled'])] final class TwigUnusedCommand { public function __invoke( #[Argument(description: 'A comma delimited list of paths to recursively search')] string $searchpaths, InputInterface $input, OutputInterface $output ): int { $data = $this->doExecute($searchpaths); $this->writeFormattedOutput($input, $output, $data); return Command::SUCCESS; } } ``` Commits ------- b57d275 [Console] Add getter for the original command "code" object
2 parents 32b347f + b57d275 commit f10093d

File tree

5 files changed

+33
-4
lines changed

5 files changed

+33
-4
lines changed

src/Symfony/Component/Console/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
7.4
55
---
66

7+
* Add `Command::getCode()` to get the code set via `setCode()`.
78
* Allow setting aliases and the hidden flag via the command name passed to the constructor
89
* Introduce `Symfony\Component\Console\Application::addCommand()` to simplify using invokable commands when the component is used standalone
910
* Deprecate `Symfony\Component\Console\Application::add()` in favor of `Symfony\Component\Console\Application::addCommand()`

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,16 @@ public function complete(CompletionInput $input, CompletionSuggestions $suggesti
344344
}
345345
}
346346

347+
/**
348+
* Gets the code that is executed by the command.
349+
*
350+
* @return ?callable null if the code has not been set with setCode()
351+
*/
352+
public function getCode(): ?callable
353+
{
354+
return $this->code?->getCode();
355+
}
356+
347357
/**
348358
* Sets the code to execute when running this command.
349359
*

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,28 @@
3030
*/
3131
class InvokableCommand implements SignalableCommandInterface
3232
{
33-
private readonly \Closure $code;
33+
private readonly \Closure $closure;
3434
private readonly ?SignalableCommandInterface $signalableCommand;
3535
private readonly \ReflectionFunction $reflection;
3636
private bool $triggerDeprecations = false;
37+
private $code;
3738

3839
public function __construct(
3940
private readonly Command $command,
4041
callable $code,
4142
) {
42-
$this->code = $this->getClosure($code);
43+
$this->code = $code;
44+
$this->closure = $this->getClosure($code);
4345
$this->signalableCommand = $code instanceof SignalableCommandInterface ? $code : null;
44-
$this->reflection = new \ReflectionFunction($this->code);
46+
$this->reflection = new \ReflectionFunction($this->closure);
4547
}
4648

4749
/**
4850
* Invokes a callable with parameters generated from the input interface.
4951
*/
5052
public function __invoke(InputInterface $input, OutputInterface $output): int
5153
{
52-
$statusCode = ($this->code)(...$this->getParameters($input, $output));
54+
$statusCode = ($this->closure)(...$this->getParameters($input, $output));
5355

5456
if (!\is_int($statusCode)) {
5557
if ($this->triggerDeprecations) {
@@ -81,6 +83,11 @@ public function configure(InputDefinition $definition): void
8183
}
8284
}
8385

86+
public function getCode(): callable
87+
{
88+
return $this->code;
89+
}
90+
8491
private function getClosure(callable $code): \Closure
8592
{
8693
if (!$code instanceof \Closure) {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,8 @@ public function testCommandAttribute()
472472
$this->assertStringContainsString('usage1', $command->getUsages()[0]);
473473
$this->assertTrue($command->isHidden());
474474
$this->assertSame(['f'], $command->getAliases());
475+
// Standard commands don't have code.
476+
$this->assertNull($command->getCode());
475477
}
476478

477479
#[IgnoreDeprecations]

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use Symfony\Component\Console\Input\InputInterface;
2828
use Symfony\Component\Console\Output\NullOutput;
2929
use Symfony\Component\Console\Output\OutputInterface;
30+
use Symfony\Component\Console\Tests\Fixtures\InvokableTestCommand;
3031

3132
class InvokableCommandTest extends TestCase
3233
{
@@ -293,6 +294,14 @@ public function __invoke()
293294
$command->run(new ArrayInput([]), new NullOutput());
294295
}
295296

297+
public function testGetCode()
298+
{
299+
$invokableTestCommand = new InvokableTestCommand();
300+
$command = new Command(null, $invokableTestCommand);
301+
302+
$this->assertSame($invokableTestCommand, $command->getCode());
303+
}
304+
296305
#[DataProvider('provideInputArguments')]
297306
public function testInputArguments(array $parameters, array $expected)
298307
{

0 commit comments

Comments
 (0)