Skip to content

Commit 5556a3a

Browse files
author
Robin Chalas
committed
feature #23510 [Console] Add a factory command loader for standalone application with lazy-loading needs (ogizanagi)
This PR was merged into the 3.4 branch. Discussion ---------- [Console] Add a factory command loader for standalone application with lazy-loading needs | Q | A | ------------- | --- | Branch? | 3.4 <!-- see comment below --> | Bug fix? | no | New feature? | yes <!-- don't forget updating src/**/CHANGELOG.md files --> | BC breaks? | no | Deprecations? | no <!-- don't forget updating UPGRADE-*.md files --> | Tests pass? | yes (failure unrelated) | Fixed tickets | #22734 (comment) <!-- #-prefixed issue number(s), if any --> | License | MIT | Doc PR | todo (with symfony/symfony-docs#8147) So standalone applications can also benefit from the lazy loading feature without requiring a PSR-11 implementation specifically for this need. The loader does not memoize any resolved command from factories, as it's the `Application` responsibility and the `ContainerCommandLoader` does not either (the PSR-11 does not enforce two successive calls to return the same value). Commits ------- 9b40b4a [Console] Add a factory command loader for standalone application with lazy-loading needs
2 parents 8c028bd + 9b40b4a commit 5556a3a

File tree

4 files changed

+134
-13
lines changed

4 files changed

+134
-13
lines changed

src/Symfony/Component/Console/CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ CHANGELOG
44
3.4.0
55
-----
66

7-
* added `CommandLoaderInterface` and PSR-11 `ContainerCommandLoader`
7+
* added `CommandLoaderInterface`, `FactoryCommandLoader` and PSR-11
8+
`ContainerCommandLoader` for commands lazy-loading
89

910
3.3.0
1011
-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Console\CommandLoader;
13+
14+
use Symfony\Component\Console\Exception\CommandNotFoundException;
15+
16+
/**
17+
* A simple command loader using factories to instantiate commands lazily.
18+
*
19+
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
20+
*/
21+
class FactoryCommandLoader implements CommandLoaderInterface
22+
{
23+
private $factories;
24+
25+
/**
26+
* @param callable[] $factories Indexed by command names
27+
*/
28+
public function __construct(array $factories)
29+
{
30+
$this->factories = $factories;
31+
}
32+
33+
/**
34+
* {@inheritdoc}
35+
*/
36+
public function has($name)
37+
{
38+
return isset($this->factories[$name]);
39+
}
40+
41+
/**
42+
* {@inheritdoc}
43+
*/
44+
public function get($name)
45+
{
46+
if (!isset($this->factories[$name])) {
47+
throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name));
48+
}
49+
50+
$factory = $this->factories[$name];
51+
52+
return $factory();
53+
}
54+
55+
/**
56+
* {@inheritdoc}
57+
*/
58+
public function getNames()
59+
{
60+
return array_keys($this->factories);
61+
}
62+
}

src/Symfony/Component/Console/Tests/ApplicationTest.php

+10-12
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Console\Application;
1616
use Symfony\Component\Console\Command\Command;
17-
use Symfony\Component\Console\CommandLoader\ContainerCommandLoader;
17+
use Symfony\Component\Console\CommandLoader\FactoryCommandLoader;
1818
use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass;
1919
use Symfony\Component\Console\Helper\HelperSet;
2020
use Symfony\Component\Console\Helper\FormatterHelper;
@@ -35,7 +35,6 @@
3535
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
3636
use Symfony\Component\Console\Exception\CommandNotFoundException;
3737
use Symfony\Component\DependencyInjection\ContainerBuilder;
38-
use Symfony\Component\DependencyInjection\ServiceLocator;
3938
use Symfony\Component\EventDispatcher\EventDispatcher;
4039

4140
class ApplicationTest extends TestCase
@@ -129,10 +128,9 @@ public function testAllWithCommandLoader()
129128
$commands = $application->all('foo');
130129
$this->assertCount(1, $commands, '->all() takes a namespace as its first argument');
131130

132-
$application->setCommandLoader(new ContainerCommandLoader(
133-
new ServiceLocator(array('foo-bar' => function () { return new \Foo1Command(); })),
134-
array('foo:bar1' => 'foo-bar')
135-
));
131+
$application->setCommandLoader(new FactoryCommandLoader(array(
132+
'foo:bar1' => function () { return new \Foo1Command(); },
133+
)));
136134
$commands = $application->all('foo');
137135
$this->assertCount(2, $commands, '->all() takes a namespace as its first argument');
138136
$this->assertInstanceOf(\FooCommand::class, $commands['foo:bar'], '->all() returns the registered commands');
@@ -202,9 +200,9 @@ public function testHasGetWithCommandLoader()
202200
$this->assertEquals($foo, $application->get('foo:bar'), '->get() returns a command by name');
203201
$this->assertEquals($foo, $application->get('afoobar'), '->get() returns a command by alias');
204202

205-
$application->setCommandLoader(new ContainerCommandLoader(new ServiceLocator(array(
206-
'foo-bar' => function () { return new \Foo1Command(); },
207-
)), array('foo:bar1' => 'foo-bar', 'afoobar1' => 'foo-bar')));
203+
$application->setCommandLoader(new FactoryCommandLoader(array(
204+
'foo:bar1' => function () { return new \Foo1Command(); },
205+
)));
208206

209207
$this->assertTrue($application->has('afoobar'), '->has() returns true if an instance is registered for an alias even with command loader');
210208
$this->assertEquals($foo, $application->get('foo:bar'), '->get() returns an instance by name even with command loader');
@@ -321,9 +319,9 @@ public function testFind()
321319
public function testFindWithCommandLoader()
322320
{
323321
$application = new Application();
324-
$application->setCommandLoader(new ContainerCommandLoader(new ServiceLocator(array(
325-
'foo-bar' => $f = function () { return new \FooCommand(); },
326-
)), array('foo:bar' => 'foo-bar')));
322+
$application->setCommandLoader(new FactoryCommandLoader(array(
323+
'foo:bar' => $f = function () { return new \FooCommand(); },
324+
)));
327325

328326
$this->assertInstanceOf('FooCommand', $application->find('foo:bar'), '->find() returns a command if its name exists');
329327
$this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $application->find('h'), '->find() returns a command if its name exists');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Console\Tests\CommandLoader;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Console\Command\Command;
16+
use Symfony\Component\Console\CommandLoader\FactoryCommandLoader;
17+
18+
class FactoryCommandLoaderTest extends TestCase
19+
{
20+
public function testHas()
21+
{
22+
$loader = new FactoryCommandLoader(array(
23+
'foo' => function () { return new Command('foo'); },
24+
'bar' => function () { return new Command('bar'); },
25+
));
26+
27+
$this->assertTrue($loader->has('foo'));
28+
$this->assertTrue($loader->has('bar'));
29+
$this->assertFalse($loader->has('baz'));
30+
}
31+
32+
public function testGet()
33+
{
34+
$loader = new FactoryCommandLoader(array(
35+
'foo' => function () { return new Command('foo'); },
36+
'bar' => function () { return new Command('bar'); },
37+
));
38+
39+
$this->assertInstanceOf(Command::class, $loader->get('foo'));
40+
$this->assertInstanceOf(Command::class, $loader->get('bar'));
41+
}
42+
43+
/**
44+
* @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException
45+
*/
46+
public function testGetUnknownCommandThrows()
47+
{
48+
(new FactoryCommandLoader(array()))->get('unknown');
49+
}
50+
51+
public function testGetCommandNames()
52+
{
53+
$loader = new FactoryCommandLoader(array(
54+
'foo' => function () { return new Command('foo'); },
55+
'bar' => function () { return new Command('bar'); },
56+
));
57+
58+
$this->assertSame(array('foo', 'bar'), $loader->getNames());
59+
}
60+
}

0 commit comments

Comments
 (0)