From dc2b84fb8dd795ea9988f396311aeed435aed495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Z=C3=A1ruba?= <79913795+janzarubadek@users.noreply.github.com> Date: Thu, 4 Jan 2024 20:53:18 +0100 Subject: [PATCH] ConsoleExtension: support command tags in NEON (#76) --- .docs/README.md | 2 + src/DI/ConsoleExtension.php | 31 ++++++++--- tests/Cases/DI/ConsoleExtension.tags.phpt | 63 +++++++++++++++++++++++ tests/Fixtures/BarCommand.php | 22 ++++++++ 4 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 tests/Cases/DI/ConsoleExtension.tags.phpt create mode 100644 tests/Fixtures/BarCommand.php diff --git a/.docs/README.md b/.docs/README.md index 520746d..fc0de73 100644 --- a/.docs/README.md +++ b/.docs/README.md @@ -92,6 +92,8 @@ services: commands.foo: class: App\FooCommand tags: [console.command: app:foo] + # or + tags: [console.command: {name: app:foo}] ``` ## Example command diff --git a/src/DI/ConsoleExtension.php b/src/DI/ConsoleExtension.php index d4093e8..d711d88 100644 --- a/src/DI/ConsoleExtension.php +++ b/src/DI/ConsoleExtension.php @@ -13,6 +13,7 @@ use Nette\Http\RequestFactory; use Nette\Schema\Expect; use Nette\Schema\Schema; +use Nette\Utils\Arrays; use stdClass; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\CommandLoader\CommandLoaderInterface; @@ -144,15 +145,29 @@ public function beforeCompile(): void // Iterate over all commands and build commandMap foreach ($commands as $serviceName => $service) { - $commandName = call_user_func([$service->getType(), 'getDefaultName']); // @phpstan-ignore-line + $tags = $service->getTags(); + $commandName = null; + + // Try to use console.command tag + if (isset($tags['console.command'])) { + if (is_string($tags['console.command'])) { + $commandName = $tags['console.command']; + } elseif (is_array($tags['console.command'])) { + $commandName = Arrays::get($tags['console.command'], 'name', null); + } + } + // Try to detect command name from Command::getDefaultName() if ($commandName === null) { - throw new ServiceCreationException( - sprintf( - 'Command "%s" missing #[AsCommand] attribute', - $service->getType(), - ) - ); + $commandName = call_user_func([$service->getType(), 'getDefaultName']); // @phpstan-ignore-line + if ($commandName === null) { + throw new ServiceCreationException( + sprintf( + 'Command "%s" missing #[AsCommand] attribute', + $service->getType(), + ) + ); + } } // Append service to command map @@ -161,7 +176,7 @@ public function beforeCompile(): void /** @var ServiceDefinition $commandLoaderDef */ $commandLoaderDef = $builder->getDefinition($this->prefix('commandLoader')); - $commandLoaderDef->getFactory()->arguments = ['@container', $commandMap]; + $commandLoaderDef->setArguments(['@container', $commandMap]); // Register event dispatcher, if available try { diff --git a/tests/Cases/DI/ConsoleExtension.tags.phpt b/tests/Cases/DI/ConsoleExtension.tags.phpt new file mode 100644 index 0000000..9aa8dcb --- /dev/null +++ b/tests/Cases/DI/ConsoleExtension.tags.phpt @@ -0,0 +1,63 @@ +withCompiler(function (Compiler $compiler): void { + $compiler->addExtension('console', new ConsoleExtension(true)); + $compiler->addConfig(Neonkit::load(<<<'NEON' + console: + services: + foo: + class: Tests\Fixtures\FooCommand + tags: [console.command: app:foo] + bar: + class: Tests\Fixtures\BarCommand + tags: [console.command: {name: app:bar}] + NEON + )); + })->build(); + + $application = $container->getByType(Application::class); + assert($application instanceof Application); + + Assert::count(2, $container->findByType(Command::class)); + Assert::same(['help', 'list', '_complete', 'completion', 'app:foo', 'app:bar'], array_keys($application->all())); +}); + +// try to set command other name +Toolkit::test(function (): void { + $container = ContainerBuilder::of() + ->withCompiler(function (Compiler $compiler): void { + $compiler->addExtension('console', new ConsoleExtension(true)); + $compiler->addConfig(Neonkit::load(<<<'NEON' + console: + services: + foo: + class: Tests\Fixtures\FooCommand + tags: [console.command: fake] + NEON + )); + })->build(); + + $application = $container->getByType(Application::class); + assert($application instanceof Application); + + Assert::exception( + fn () => $application->all(), + CommandNotFoundException::class, + 'The "fake" command cannot be found because it is registered under multiple names. Make sure you don\'t set a different name via constructor or "setName()".' + ); +}); diff --git a/tests/Fixtures/BarCommand.php b/tests/Fixtures/BarCommand.php new file mode 100644 index 0000000..fda07f3 --- /dev/null +++ b/tests/Fixtures/BarCommand.php @@ -0,0 +1,22 @@ +