From 3c87d39e72c6f1784094e544f6c836147d276518 Mon Sep 17 00:00:00 2001 From: Moshe Weitzman Date: Wed, 9 Jul 2025 07:25:15 -0400 Subject: [PATCH 1/9] Make Attributes accessible on Invokable Commands --- src/Symfony/Component/Console/Command/Command.php | 5 +++++ .../Component/Console/Command/InvokableCommand.php | 13 ++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index 1d2e12bdcce2..2d31393f2fef 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -356,6 +356,11 @@ public function complete(CompletionInput $input, CompletionSuggestions $suggesti } } + public function getCode(): ?InvokableCommand + { + return $this->code; + } + /** * Sets the code to execute when running this command. * diff --git a/src/Symfony/Component/Console/Command/InvokableCommand.php b/src/Symfony/Component/Console/Command/InvokableCommand.php index 72ff407c81fd..f9181b8b57f1 100644 --- a/src/Symfony/Component/Console/Command/InvokableCommand.php +++ b/src/Symfony/Component/Console/Command/InvokableCommand.php @@ -34,13 +34,15 @@ class InvokableCommand implements SignalableCommandInterface private readonly ?SignalableCommandInterface $signalableCommand; private readonly \ReflectionFunction $reflection; private bool $triggerDeprecations = false; + private $callable; public function __construct( private readonly Command $command, - callable $code, + callable $callable, ) { - $this->code = $this->getClosure($code); - $this->signalableCommand = $code instanceof SignalableCommandInterface ? $code : null; + $this->callable = $callable; + $this->code = $this->getClosure($callable); + $this->signalableCommand = $callable instanceof SignalableCommandInterface ? $callable : null; $this->reflection = new \ReflectionFunction($this->code); } @@ -81,6 +83,11 @@ public function configure(InputDefinition $definition): void } } + public function getCallable(): callable + { + return $this->callable; + } + private function getClosure(callable $code): \Closure { if (!$code instanceof \Closure) { From f8a0c3455012c1ad12c3b71432be7ef5197dbe10 Mon Sep 17 00:00:00 2001 From: Moshe Weitzman Date: Wed, 9 Jul 2025 11:38:08 -0400 Subject: [PATCH 2/9] Dont return type hint to an internal class --- src/Symfony/Component/Console/Command/Command.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index 2d31393f2fef..2527a237cce3 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -356,7 +356,10 @@ public function complete(CompletionInput $input, CompletionSuggestions $suggesti } } - public function getCode(): ?InvokableCommand + /** + * Get the InvokableCommand instance if it exists. + */ + public function getCode() { return $this->code; } From 61b375e1306c3db55ad86eabd12f4090eedd1667 Mon Sep 17 00:00:00 2001 From: Moshe Weitzman Date: Wed, 9 Jul 2025 20:31:08 -0400 Subject: [PATCH 3/9] Add a test --- .../Component/Console/Tests/Command/CommandTest.php | 2 ++ .../Console/Tests/Command/InvokableCommandTest.php | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/Symfony/Component/Console/Tests/Command/CommandTest.php b/src/Symfony/Component/Console/Tests/Command/CommandTest.php index a4a719b3d10a..fd7755ca381c 100644 --- a/src/Symfony/Component/Console/Tests/Command/CommandTest.php +++ b/src/Symfony/Component/Console/Tests/Command/CommandTest.php @@ -469,6 +469,8 @@ public function testCommandAttribute() $this->assertStringContainsString('usage1', $command->getUsages()[0]); $this->assertTrue($command->isHidden()); $this->assertSame(['f'], $command->getAliases()); + // Standard commands don't have code. + $this->assertNull($command->getCode()); } /** diff --git a/src/Symfony/Component/Console/Tests/Command/InvokableCommandTest.php b/src/Symfony/Component/Console/Tests/Command/InvokableCommandTest.php index 8bd0dceb4425..9049413f01b7 100644 --- a/src/Symfony/Component/Console/Tests/Command/InvokableCommandTest.php +++ b/src/Symfony/Component/Console/Tests/Command/InvokableCommandTest.php @@ -16,6 +16,7 @@ use Symfony\Component\Console\Attribute\Argument; use Symfony\Component\Console\Attribute\Option; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Command\InvokableCommand; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Completion\Suggestion; @@ -26,6 +27,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\NullOutput; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tests\Fixtures\InvokableTestCommand; class InvokableCommandTest extends TestCase { @@ -292,6 +294,16 @@ public function __invoke() $command->run(new ArrayInput([]), new NullOutput()); } + public function testGetCode() + { + // Create a command from an invokable class. + $command = new Command(null, new InvokableTestCommand()); + + // Ensure that the invokable class can be retrieved from the Command. + $this->assertInstanceOf(InvokableCommand::class, $command->getCode()); + $this->assertInstanceOf(InvokableTestCommand::class, $command->getCode()->getCallable()); + } + /** * @dataProvider provideInputArguments */ From 735528b41142d01abe6c2ca99e9af3f6f3b12196 Mon Sep 17 00:00:00 2001 From: Moshe Weitzman Date: Thu, 10 Jul 2025 06:19:27 -0400 Subject: [PATCH 4/9] Get the callable instead MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jérôme Tamarelle --- src/Symfony/Component/Console/Command/Command.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index 2527a237cce3..20fbf37a98ad 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -359,9 +359,9 @@ public function complete(CompletionInput $input, CompletionSuggestions $suggesti /** * Get the InvokableCommand instance if it exists. */ - public function getCode() + public function getCode(): ?callable { - return $this->code; + return $this->code?->getCallable(); } /** From bc80c6744e41034ec207aec268d90d517c37d22c Mon Sep 17 00:00:00 2001 From: Moshe Weitzman Date: Thu, 10 Jul 2025 06:19:46 -0400 Subject: [PATCH 5/9] Now only 1 assertion needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jérôme Tamarelle --- .../Component/Console/Tests/Command/InvokableCommandTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Symfony/Component/Console/Tests/Command/InvokableCommandTest.php b/src/Symfony/Component/Console/Tests/Command/InvokableCommandTest.php index 9049413f01b7..0a130d13496f 100644 --- a/src/Symfony/Component/Console/Tests/Command/InvokableCommandTest.php +++ b/src/Symfony/Component/Console/Tests/Command/InvokableCommandTest.php @@ -300,8 +300,7 @@ public function testGetCode() $command = new Command(null, new InvokableTestCommand()); // Ensure that the invokable class can be retrieved from the Command. - $this->assertInstanceOf(InvokableCommand::class, $command->getCode()); - $this->assertInstanceOf(InvokableTestCommand::class, $command->getCode()->getCallable()); + $this->assertInstanceOf(InvokableTestCommand::class, $command->getCode()); } /** From 2eb1155f6d4c59ef0e8951e3e100748bdd15b232 Mon Sep 17 00:00:00 2001 From: Moshe Weitzman Date: Thu, 10 Jul 2025 06:25:15 -0400 Subject: [PATCH 6/9] Remove unused import --- .../Component/Console/Tests/Command/InvokableCommandTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Symfony/Component/Console/Tests/Command/InvokableCommandTest.php b/src/Symfony/Component/Console/Tests/Command/InvokableCommandTest.php index 0a130d13496f..f7b947db3358 100644 --- a/src/Symfony/Component/Console/Tests/Command/InvokableCommandTest.php +++ b/src/Symfony/Component/Console/Tests/Command/InvokableCommandTest.php @@ -16,7 +16,6 @@ use Symfony\Component\Console\Attribute\Argument; use Symfony\Component\Console\Attribute\Option; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Command\InvokableCommand; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Completion\Suggestion; From 36d162fd2ea4c2f7459213540b8a390e7e566578 Mon Sep 17 00:00:00 2001 From: Moshe Weitzman Date: Thu, 10 Jul 2025 07:35:18 -0400 Subject: [PATCH 7/9] Imrove code comment --- src/Symfony/Component/Console/Command/Command.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index 20fbf37a98ad..9e94b2b9fa21 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -357,7 +357,7 @@ public function complete(CompletionInput $input, CompletionSuggestions $suggesti } /** - * Get the InvokableCommand instance if it exists. + * Get the callable from the InvokableCommand if available. */ public function getCode(): ?callable { From 7f1687d107152abb7a39bf971bbb9b6e6c05165d Mon Sep 17 00:00:00 2001 From: Moshe Weitzman Date: Thu, 10 Jul 2025 08:13:38 -0400 Subject: [PATCH 8/9] Omit internal class name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jérôme Tamarelle --- src/Symfony/Component/Console/Command/Command.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index 9e94b2b9fa21..481ef368205d 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -357,7 +357,7 @@ public function complete(CompletionInput $input, CompletionSuggestions $suggesti } /** - * Get the callable from the InvokableCommand if available. + * Get the code that is executed by the command. */ public function getCode(): ?callable { From cb694ee9ba666c9fc8400c1c626b3613c17d3c5a Mon Sep 17 00:00:00 2001 From: Moshe Weitzman Date: Thu, 10 Jul 2025 12:22:11 -0400 Subject: [PATCH 9/9] Rename to code and closure --- .../Component/Console/Command/Command.php | 2 +- .../Console/Command/InvokableCommand.php | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index 481ef368205d..d8f6d7358029 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -361,7 +361,7 @@ public function complete(CompletionInput $input, CompletionSuggestions $suggesti */ public function getCode(): ?callable { - return $this->code?->getCallable(); + return $this->code?->getCode(); } /** diff --git a/src/Symfony/Component/Console/Command/InvokableCommand.php b/src/Symfony/Component/Console/Command/InvokableCommand.php index f9181b8b57f1..b497f4df73bf 100644 --- a/src/Symfony/Component/Console/Command/InvokableCommand.php +++ b/src/Symfony/Component/Console/Command/InvokableCommand.php @@ -30,20 +30,20 @@ */ class InvokableCommand implements SignalableCommandInterface { - private readonly \Closure $code; + private readonly \Closure $closure; private readonly ?SignalableCommandInterface $signalableCommand; private readonly \ReflectionFunction $reflection; private bool $triggerDeprecations = false; - private $callable; + private $code; public function __construct( private readonly Command $command, - callable $callable, + callable $code, ) { - $this->callable = $callable; - $this->code = $this->getClosure($callable); - $this->signalableCommand = $callable instanceof SignalableCommandInterface ? $callable : null; - $this->reflection = new \ReflectionFunction($this->code); + $this->code = $code; + $this->closure = $this->getClosure($code); + $this->signalableCommand = $code instanceof SignalableCommandInterface ? $code : null; + $this->reflection = new \ReflectionFunction($this->closure); } /** @@ -51,7 +51,7 @@ public function __construct( */ public function __invoke(InputInterface $input, OutputInterface $output): int { - $statusCode = ($this->code)(...$this->getParameters($input, $output)); + $statusCode = ($this->closure)(...$this->getParameters($input, $output)); if (!\is_int($statusCode)) { if ($this->triggerDeprecations) { @@ -83,9 +83,9 @@ public function configure(InputDefinition $definition): void } } - public function getCallable(): callable + public function getCode(): callable { - return $this->callable; + return $this->code; } private function getClosure(callable $code): \Closure