8000 [Console] Add support for command lazy-loading · symfony/symfony@6461a31 · GitHub
[go: up one dir, main page]

Skip to content

Commit 6461a31

Browse files
committed
[Console] Add support for command lazy-loading
1 parent 27852a0 commit 6461a31

File tree

15 files changed

+381
-44
lines changed

15 files changed

+381
-44
lines changed

src/Symfony/Bundle/FrameworkBundle/Console/Application.php

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,12 @@ public function doRun(InputInterface $input, OutputInterface $output)
6868
$this->kernel->boot();
6969

7070
$container = $this->kernel->getContainer();
71+
$this->setDispatcher($container->get('event_dispatcher'));
7172

72-
foreach ($this->all() as $command) {
73-
if ($command instanceof ContainerAwareInterface) {
74-
$command->setContainer($container);
75-
}
73+
if ($container->has('console.command_loader')) {
74+
$this->setCommandLoader($container->get('console.command_loader'));
7675
}
7776

78-
$this->setDispatcher($container->get('event_dispatcher'));
79-
8077
return parent::doRun($input, $output);
8178
}
8279

@@ -97,7 +94,13 @@ public function get($name)
9794
{
9895
$this->registerCommands();
9996

100-
return parent::get($name);
97+
$command = parent::get($name);
98+
99+
if ($command instanceof ContainerAwareInterface) {
100+
$command->setContainer($this->kernel->getContainer());
101+
}
102+
103+
return $< F438 /span>command;
101104
}
102105

103106
/**
@@ -136,9 +139,12 @@ protected function registerCommands()
136139
}
137140
}
138141

142+
// @deprecated since version 3.4, to be removed in 4.0
139143
if ($container->hasParameter('console.command.ids')) {
140144
foreach ($container->getParameter('console.command.ids') as $id) {
141-
$this->add($container->get($id));
145+
if (false !== $id) {
146+
$this->add($container->get($id));
147+
}
142148
}
143149
}
144150
}

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
"fig/link-util": "^1.0",
3737
"symfony/asset": "~3.3|~4.0.0",
3838
"symfony/browser-kit": "~2.8|~3.0|~4.0.0",
39-
"symfony/console": "~3.3|~4.0.0",
39+
"symfony/console": "~3.4|~4.0.0",
4040
"symfony/css-selector": "~2.8|~3.0|~4.0.0",
4141
"symfony/dom-crawler": "~2.8|~3.0|~4.0.0",
4242
"symfony/polyfill-intl-icu": "~1.0",
@@ -64,7 +64,7 @@
6464
"phpdocumentor/type-resolver": "<0.2.0",
6565
"phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0",
6666
"symfony/asset": "<3.3",
67-
"symfony/console": "<3.3",
67+
"symfony/console": "<3.4",
6868
"symfony/form": "<3.3",
6969
"symfony/property-info": "<3.3",
7070
"symfony/serializer": "<3.3",

src/Symfony/Bundle/SecurityBundle/Resources/config/console.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<service id="security.console.user_password_encoder_command" class="Symfony\Bundle\SecurityBundle\Command\UserPasswordEncoderCommand">
1111
<argument type="service" id="security.encoder_factory"/>
1212
<argument type="collection" /> <!-- encoders' user classes -->
13-
<tag name="console.command" />
13+
<tag name="console.command" command="security:encode-password" />
1414
</service>
1515
</services>
1616
</container>

src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,9 @@ protected function setUp()
222222
$kernel->boot();
223223

224224
$application = new Application($kernel);
225-
225+
if ($kernel->getContainer()->has('console.command_loader')) {
226+
$application->setCommandLoader($kernel->getContainer()->get('console.command_loader'));
227+
}
226228
$passwordEncoderCommand = $application->get('security:encode-password');
227229

228230
$this->passwordEncoderCommandTester = new CommandTester($passwordEncoderCommand);

src/Symfony/Bundle/SecurityBundle/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
],
1818
"require": {
1919
"php": ">=5.5.9",
20-
"symfony/security": "~3.3|~4.0.0",
20+
"symfony/security": "~3.4|~4.0.0",
2121
"symfony/dependency-injection": "~3.3-beta2|~4.0.0",
2222
"symfony/http-kernel": "~3.3|~4.0.0",
2323
"symfony/polyfill-php70": "~1.0"

src/Symfony/Bundle/WebServerBundle/Resources/config/webserver.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,21 @@
1010
<service id="web_server.command.server_run" class="Symfony\Bundle\WebServerBundle\Command\ServerRunCommand">
1111
<argument>%kernel.root_dir%/../web</argument>
1212
<argument>%kernel.environment%</argument>
13-
<tag name="console.command" />
13+
<tag name="console.command" command="server:run" />
1414
</service>
1515

1616
<service id="web_server.command.server_start" class="Symfony\Bundle\WebServerBundle\Command\ServerStartCommand">
1717
<argument>%kernel.root_dir%/../web</argument>
1818
<argument>%kernel.environment%</argument>
19-
<tag name="console.command" />
19+
<tag name="console.command" command="server:start" />
2020
</service>
2121

2222
<service id="web_server.command.server_stop" class="Symfony\Bundle\WebServerBundle\Command\ServerStopCommand">
23-
<tag name="console.command" />
23+
<tag name="console.command" command="server:stop" />
2424
</service>
2525

2626
<service id="web_server.command.server_status" class="Symfony\Bundle\WebServerBundle\Command\ServerStatusCommand">
27-
<tag name="console.command" />
27+
<tag name="console.command" command="server:status" />
2828
</service>
2929
</services>
3030
</container>

src/Symfony/Component/Console/Application.php

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Console;
1313

14+
use Symfony\Component\Console\CommandLoader\CommandLoaderInterface;
1415
use Symfony\Component\Console\Exception\ExceptionInterface;
1516
use Symfony\Component\Console\Formatter\OutputFormatter;
1617
use Symfony\Component\Console\Helper\DebugFormatterHelper;
@@ -64,6 +65,7 @@ class Application
6465
private $runningCommand;
6566
private $name;
6667
private $version;
68+
private $commandLoader;
6769
private $catchExceptions = true;
6870
private $autoExit = true;
6971
private $definition;
@@ -96,6 +98,11 @@ public function setDispatcher(EventDispatcherInterface $dispatcher)
9698
$this->dispatcher = $dispatcher;
9799
}
98100

101+
public function setCommandLoader(CommandLoaderInterface $commandLoader)
102+
{
103+
$this->commandLoader = $commandLoader;
104+
}
105+
99106
/**
100107
* Runs the current application.
101108
*
@@ -431,6 +438,10 @@ public function add(Command $command)
431438
throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command)));
432439
}
433440

441+
if (!$command->getName()) {
442+
throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($command)));
443+
}
444+
434445
$this->commands[$command->getName()] = $command;
435446

436447
foreach ($command->getAliases() as $alias) {
@@ -451,12 +462,16 @@ public function add(Command $command)
451462
*/
452463
public function get($name)
453464
{
454-
if (!isset($this->commands[$name])) {
465+
if (isset($this->commands[$name])) {
466+
$command = $this->commands[$name];
467+
} elseif ($this->commandLoader && $this->commandLoader->has($name)) {
468+
$command = $this->commandLoader->get($name);
469+
$command->setName($name);
470+
$this->add($command);
471+
} else {
455472
throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name));
456473
}
457474

458-
$command = $this->commands[$name];
459-
460475
if ($this->wantHelps) {
461476
$this->wantHelps = false;
462477

@@ -478,7 +493,7 @@ public function get($name)
478493
*/
479494
public function has($name)
480495
{
481-
return isset($this->commands[$name]);
496+
return isset($this->commands[$name]) || ($this->commandLoader && $this->commandLoader->has($name));
482497
}
483498

484499
/**
@@ -555,7 +570,7 @@ public function findNamespace($namespace)
555570
*/
556571
public function find($name)
557572
{
558-
$allCommands = array_keys($this->commands);
573+
$allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands);
559574
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name);
560575
$commands = preg_grep('{^'.$expr.'}', $allCommands);
561576

@@ -581,12 +596,12 @@ public function find($name)
581596

582597
// filter out aliases for commands which are already on the list
583598
if (count($commands) > 1) {
584-
$commandList = $this->commands;
585-
$commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) {
586-
$commandName = $commandList[$nameOrAlias]->getName();
599+
$commandList = $this->commandLoader ? array_merge(array_flip($this->commandLoader->getNames()), $this->commands) : $this->commands;
600+
$commands = array_unique(array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) {
601+
$commandName = $commandList[$nameOrAlias] instanceof Command ? $commandList[$nameOrAlias]->getName() : $nameOrAlias;
587602

588603
return $commandName === $nameOrAlias || !in_array($commandName, $commands);
589-
});
604+
}));
590605
}
591606

592607
$exact = in_array($name, $commands, true);
@@ -598,6 +613,9 @@ public function find($name)
598613
$maxLen = max(Helper::strlen($abbrev), $maxLen);
599614
}
600615
$abbrevs = array_map(function ($cmd) use ($commandList, $usableWidth, $maxLen) {
616+
if (!$commandList[$cmd] instanceof Command) {
617+
return $cmd;
618+
}
601619
$abbrev = str_pad($cmd, $maxLen, ' ').' '.$commandList[$cmd]->getDescription();
602620

603621
return Helper::strlen($abbrev) > $usableWidth ? Helper::substr($abbrev, 0, $usableWidth - 3).'...' : $abbrev;
@@ -622,7 +640,18 @@ public function find($name)
622640
public function all($namespace = null)
623641
{
624642
if (null === $namespace) {
625-
return $this->commands;
643+
if (!$this->commandLoader) {
644+
return $this->commands;
645+
}
646+
647+
$commands = $this->commands;
648+
foreach ($this->commandLoader->getNames() as $name) {
649+
if (!isset($commands[$name])) {
650+
$commands[$name] = $this->commandLoader->get($name);
651+
}
652+
}
653+
654+
return $commands;
626655
}
627656

628657
$commands = array();
@@ -632,6 +661,14 @@ public function all($namespace = null)
632661
}
633662
}
634663

664+
if ($this->commandLoader) {
665+
foreach ($this->commandLoader->getNames() as $name) {
666+
if (!isset($commands[$name]) && $namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) {
667+
$commands[$name] = $this->commandLoader->get($name);
668+
}
669+
}
670+
}
671+
635672
return $commands;
636673
}
637674

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,6 @@ public function __construct($name = null)
6161
}
6262

6363
$this->configure();
64-
65-
if (!$this->name) {
66-
throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($this)));
67-
}
6864
}
6965

7066
/**
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace Symfony\Component\Console\CommandLoader;
4+
5+
use Symfony\Component\Console\Command\Command;
6+
use Symfony\Component\Console\Exception\CommandNotFoundException;
7+
8+
/**
9+
* @author Robin Chalas <robin.chalas@gmail.com>
10+
*/
11+
interface CommandLoaderInterface
12+
{
13+
/**
14+
* Loads a command.
15+
*
16+
* @param string $namee
17+
*
18+
* @return Command
19+
*
20+
* @throws CommandNotFoundException
21+
*/
22+
public function get($name);
23+
24+
/**
25+
* Checks if a command exists
26+
*
27+
* @param string $name
28+
*
29+
* @return bool
30+
*/
31+
public function has($name);
32+
33+
/**
34+
* @return iterable All registered commands mapped by names
35+
*/
36+
public function all();
37+
38+
/**
39+
* @return string[] All registered command names
40+
*/
41+
public function getNames();
42+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
namespace Symfony\Component\Console\CommandLoader;
4+
5+
use Psr\Container\ContainerInterface;
6+
use Symfony\Component\Console\Exception\CommandNotFoundException;
7+
8+
/**
9+
* Loads commands from a PSR-11 container.
10+
*
11+
* @author Robin Chalas <robin.chalas@gmail.com>
12+
*/
13+
class ContainerCommandLoader implements CommandLoaderInterface
14+
{
15+
private $container;
16+
private $commandNames;
17+
18+
public function __construct(ContainerInterface $container, array $commandNames)
19+
{
20+
$this->container = $container;
21+
$this->commandNames = $commandNames;
22+
}
23+
24+
/**
25+
* {@inheritdoc}
26+
*/
27+
public function get($name)
28+
{
29+
if (!$this->has($name)) {
30+
throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name));
31+
}
32+
33+
return $this->container->get($name);
34+
}
35+
36+
/**
37+
* {@inheritdoc}
38+
*/
39+
public function has($name)
40+
{
41+
return in_array($name, $this->commandNames, true);
42+
}
43+
44+
/**
45+
* {@inheritdoc}
46+
*/
47+
public function all()
48+
{
49+
foreach ($this->commandNames as $name) {
50+
yield $name => $this->container->get($name);
51+
}
52+
}
53+
54+
/**
55+
* {@inheritdoc}
56+
*/
57+
public function getNames()
58+
{
59+
return $this->commandNames;
60+
}
61+
}

0 commit comments

Comments
 (0)
0