Skip to content

Commit

Permalink
ConsoleExtension: support command tags in NEON (#76)
Browse files Browse the repository at this point in the history
  • Loading branch information
janzarubadek authored and f3l1x committed Jan 4, 2024
1 parent e52267a commit dc2b84f
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 8 deletions.
2 changes: 2 additions & 0 deletions .docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ services:
commands.foo:
class: App\FooCommand
tags: [console.command: app:foo]
# or
tags: [console.command: {name: app:foo}]
```

## Example command
Expand Down
31 changes: 23 additions & 8 deletions src/DI/ConsoleExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand Down
63 changes: 63 additions & 0 deletions tests/Cases/DI/ConsoleExtension.tags.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php declare(strict_types = 1);

use Contributte\Console\Application;
use Contributte\Console\DI\ConsoleExtension;
use Contributte\Tester\Toolkit;
use Contributte\Tester\Utils\ContainerBuilder;
use Contributte\Tester\Utils\Neonkit;
use Nette\DI\Compiler;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\CommandNotFoundException;
use Tester\Assert;

require_once __DIR__ . '/../../bootstrap.php';

// console.command
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: 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()".'
);
});
22 changes: 22 additions & 0 deletions tests/Fixtures/BarCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php declare(strict_types = 1);

namespace Tests\Fixtures;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(
name: 'app:bar',
description: 'Bar command',
)]
final class BarCommand extends Command
{

protected function execute(InputInterface $input, OutputInterface $output): int
{
return 0;
}

}

0 comments on commit dc2b84f

Please sign in to comment.