Skip to content

Commit dc2b84f

Browse files
janzarubadekf3l1x
authored andcommitted
ConsoleExtension: support command tags in NEON (#76)
1 parent e52267a commit dc2b84f

File tree

4 files changed

+110
-8
lines changed

4 files changed

+110
-8
lines changed

.docs/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ services:
9292
commands.foo:
9393
class: App\FooCommand
9494
tags: [console.command: app:foo]
95+
# or
96+
tags: [console.command: {name: app:foo}]
9597
```
9698

9799
## Example command

src/DI/ConsoleExtension.php

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Nette\Http\RequestFactory;
1414
use Nette\Schema\Expect;
1515
use Nette\Schema\Schema;
16+
use Nette\Utils\Arrays;
1617
use stdClass;
1718
use Symfony\Component\Console\Command\Command;
1819
use Symfony\Component\Console\CommandLoader\CommandLoaderInterface;
@@ -144,15 +145,29 @@ public function beforeCompile(): void
144145

145146
// Iterate over all commands and build commandMap
146147
foreach ($commands as $serviceName => $service) {
147-
$commandName = call_user_func([$service->getType(), 'getDefaultName']); // @phpstan-ignore-line
148+
$tags = $service->getTags();
149+
$commandName = null;
150+
151+
// Try to use console.command tag
152+
if (isset($tags['console.command'])) {
153+
if (is_string($tags['console.command'])) {
154+
$commandName = $tags['console.command'];
155+
} elseif (is_array($tags['console.command'])) {
156+
$commandName = Arrays::get($tags['console.command'], 'name', null);
157+
}
158+
}
148159

160+
// Try to detect command name from Command::getDefaultName()
149161
if ($commandName === null) {
150-
throw new ServiceCreationException(
151-
sprintf(
152-
'Command "%s" missing #[AsCommand] attribute',
153-
$service->getType(),
154-
)
155-
);
162+
$commandName = call_user_func([$service->getType(), 'getDefaultName']); // @phpstan-ignore-line
163+
if ($commandName === null) {
164+
throw new ServiceCreationException(
165+
sprintf(
166+
'Command "%s" missing #[AsCommand] attribute',
167+
$service->getType(),
168+
)
169+
);
170+
}
156171
}
157172

158173
// Append service to command map
@@ -161,7 +176,7 @@ public function beforeCompile(): void
161176

162177
/** @var ServiceDefinition $commandLoaderDef */
163178
$commandLoaderDef = $builder->getDefinition($this->prefix('commandLoader'));
164-
$commandLoaderDef->getFactory()->arguments = ['@container', $commandMap];
179+
$commandLoaderDef->setArguments(['@container', $commandMap]);
165180

166181
// Register event dispatcher, if available
167182
try {
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php declare(strict_types = 1);
2+
3+
use Contributte\Console\Application;
4+
use Contributte\Console\DI\ConsoleExtension;
5+
use Contributte\Tester\Toolkit;
6+
use Contributte\Tester\Utils\ContainerBuilder;
7+
use Contributte\Tester\Utils\Neonkit;
8+
use Nette\DI\Compiler;
9+
use Symfony\Component\Console\Command\Command;
10+
use Symfony\Component\Console\Exception\CommandNotFoundException;
11+
use Tester\Assert;
12+
13+
require_once __DIR__ . '/../../bootstrap.php';
14+
15+
// console.command
16+
Toolkit::test(function (): void {
17+
$container = ContainerBuilder::of()
18+
->withCompiler(function (Compiler $compiler): void {
19+
$compiler->addExtension('console', new ConsoleExtension(true));
20+
$compiler->addConfig(Neonkit::load(<<<'NEON'
21+
console:
22+
services:
23+
foo:
24+
class: Tests\Fixtures\FooCommand
25+
tags: [console.command: app:foo]
26+
bar:
27+
class: Tests\Fixtures\BarCommand
28+
tags: [console.command: {name: app:bar}]
29+
NEON
30+
));
31+
})->build();
32+
33+
$application = $container->getByType(Application::class);
34+
assert($application instanceof Application);
35+
36+
Assert::count(2, $container->findByType(Command::class));
37+
Assert::same(['help', 'list', '_complete', 'completion', 'app:foo', 'app:bar'], array_keys($application->all()));
38+
});
39+
40+
// try to set command other name
41+
Toolkit::test(function (): void {
42+
$container = ContainerBuilder::of()
43+
->withCompiler(function (Compiler $compiler): void {
44+
$compiler->addExtension('console', new ConsoleExtension(true));
45+
$compiler->addConfig(Neonkit::load(<<<'NEON'
46+
console:
47+
services:
48+
foo:
49+
class: Tests\Fixtures\FooCommand
50+
tags: [console.command: fake]
51+
NEON
52+
));
53+
})->build();
54+
55+
$application = $container->getByType(Application::class);
56+
assert($application instanceof Application);
57+
58+
Assert::exception(
59+
fn () => $application->all(),
60+
CommandNotFoundException::class,
61+
'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()".'
62+
);
63+
});

tests/Fixtures/BarCommand.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Tests\Fixtures;
4+
5+
use Symfony\Component\Console\Attribute\AsCommand;
6+
use Symfony\Component\Console\Command\Command;
7+
use Symfony\Component\Console\Input\InputInterface;
8+
use Symfony\Component\Console\Output\OutputInterface;
9+
10+
#[AsCommand(
11+
name: 'app:bar',
12+
description: 'Bar command',
13+
)]
14+
final class BarCommand extends Command
15+
{
16+
17+
protected function execute(InputInterface $input, OutputInterface $output): int
18+
{
19+
return 0;
20+
}
21+
22+
}

0 commit comments

Comments
 (0)