[DependencyInjection] Invokable Factory Services
This commit is contained in:
parent
3895acd175
commit
23cb83f726
@ -95,7 +95,7 @@ class Definition
|
|||||||
/**
|
/**
|
||||||
* Sets a factory.
|
* Sets a factory.
|
||||||
*
|
*
|
||||||
* @param string|array $factory A PHP function or an array containing a class/Reference and a method to call
|
* @param string|array|Reference $factory A PHP function, reference or an array containing a class/Reference and a method to call
|
||||||
*
|
*
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
@ -105,6 +105,8 @@ class Definition
|
|||||||
|
|
||||||
if (\is_string($factory) && false !== strpos($factory, '::')) {
|
if (\is_string($factory) && false !== strpos($factory, '::')) {
|
||||||
$factory = explode('::', $factory, 2);
|
$factory = explode('::', $factory, 2);
|
||||||
|
} elseif ($factory instanceof Reference) {
|
||||||
|
$factory = [$factory, '__invoke'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->factory = $factory;
|
$this->factory = $factory;
|
||||||
@ -782,7 +784,7 @@ class Definition
|
|||||||
/**
|
/**
|
||||||
* Sets a configurator to call after the service is fully initialized.
|
* Sets a configurator to call after the service is fully initialized.
|
||||||
*
|
*
|
||||||
* @param string|array $configurator A PHP callable
|
* @param string|array|Reference $configurator A PHP function, reference or an array containing a class/Reference and a method to call
|
||||||
*
|
*
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
@ -792,6 +794,8 @@ class Definition
|
|||||||
|
|
||||||
if (\is_string($configurator) && false !== strpos($configurator, '::')) {
|
if (\is_string($configurator) && false !== strpos($configurator, '::')) {
|
||||||
$configurator = explode('::', $configurator, 2);
|
$configurator = explode('::', $configurator, 2);
|
||||||
|
} elseif ($configurator instanceof Reference) {
|
||||||
|
$configurator = [$configurator, '__invoke'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->configurator = $configurator;
|
$this->configurator = $configurator;
|
||||||
|
@ -317,7 +317,7 @@ class XmlFileLoader extends FileLoader
|
|||||||
$class = $factory->hasAttribute('class') ? $factory->getAttribute('class') : null;
|
$class = $factory->hasAttribute('class') ? $factory->getAttribute('class') : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$definition->setFactory([$class, $factory->getAttribute('method')]);
|
$definition->setFactory([$class, $factory->getAttribute('method') ?: '__invoke']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,7 +332,7 @@ class XmlFileLoader extends FileLoader
|
|||||||
$class = $configurator->getAttribute('class');
|
$class = $configurator->getAttribute('class');
|
||||||
}
|
}
|
||||||
|
|
||||||
$definition->setConfigurator([$class, $configurator->getAttribute('method')]);
|
$definition->setConfigurator([$class, $configurator->getAttribute('method') ?: '__invoke']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -571,12 +571,15 @@ class YamlFileLoader extends FileLoader
|
|||||||
*
|
*
|
||||||
* @throws InvalidArgumentException When errors occur
|
* @throws InvalidArgumentException When errors occur
|
||||||
*
|
*
|
||||||
* @return string|array A parsed callable
|
* @return string|array|Reference A parsed callable
|
||||||
*/
|
*/
|
||||||
private function parseCallable($callable, $parameter, $id, $file)
|
private function parseCallable($callable, $parameter, $id, $file)
|
||||||
{
|
{
|
||||||
if (\is_string($callable)) {
|
if (\is_string($callable)) {
|
||||||
if ('' !== $callable && '@' === $callable[0]) {
|
if ('' !== $callable && '@' === $callable[0]) {
|
||||||
|
if (false === strpos($callable, ':')) {
|
||||||
|
return [$this->resolveServices($callable, $file), '__invoke'];
|
||||||
|
}
|
||||||
throw new InvalidArgumentException(sprintf('The value of the "%s" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s").', $parameter, $id, $callable, substr($callable, 1)));
|
throw new InvalidArgumentException(sprintf('The value of the "%s" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s").', $parameter, $id, $callable, substr($callable, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\DependencyInjection\Tests;
|
|||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\DependencyInjection\Definition;
|
use Symfony\Component\DependencyInjection\Definition;
|
||||||
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
|
|
||||||
class DefinitionTest extends TestCase
|
class DefinitionTest extends TestCase
|
||||||
{
|
{
|
||||||
@ -35,6 +36,9 @@ class DefinitionTest extends TestCase
|
|||||||
|
|
||||||
$def->setFactory('Foo::bar');
|
$def->setFactory('Foo::bar');
|
||||||
$this->assertEquals(['Foo', 'bar'], $def->getFactory(), '->setFactory() converts string static method call to the array');
|
$this->assertEquals(['Foo', 'bar'], $def->getFactory(), '->setFactory() converts string static method call to the array');
|
||||||
|
|
||||||
|
$def->setFactory($ref = new Reference('baz'));
|
||||||
|
$this->assertSame([$ref, '__invoke'], $def->getFactory(), '->setFactory() converts service reference to class invoke call');
|
||||||
$this->assertSame(['factory' => true], $def->getChanges());
|
$this->assertSame(['factory' => true], $def->getChanges());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +59,9 @@
|
|||||||
<service id="new_factory4" class="BazClass">
|
<service id="new_factory4" class="BazClass">
|
||||||
<factory method="getInstance" />
|
<factory method="getInstance" />
|
||||||
</service>
|
</service>
|
||||||
|
<service id="new_factory5" class="FooBarClass">
|
||||||
|
<factory service="baz" />
|
||||||
|
</service>
|
||||||
<service id="alias_for_foo" alias="foo" />
|
<service id="alias_for_foo" alias="foo" />
|
||||||
<service id="another_alias_for_foo" alias="foo" public="false" />
|
<service id="another_alias_for_foo" alias="foo" public="false" />
|
||||||
<service id="0" class="FooClass" />
|
<service id="0" class="FooClass" />
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
services:
|
||||||
|
factory:
|
||||||
|
class: Baz
|
||||||
|
invalid_factory:
|
||||||
|
class: FooBarClass
|
||||||
|
factory: '@factory:method'
|
@ -1,3 +1,4 @@
|
|||||||
services:
|
services:
|
||||||
factory: { class: FooBarClass, factory: baz:getClass}
|
factory: { class: FooBarClass, factory: baz:getClass}
|
||||||
factory_with_static_call: { class: FooBarClass, factory: FooBacFactory::createFooBar}
|
factory_with_static_call: { class: FooBarClass, factory: FooBacFactory::createFooBar}
|
||||||
|
invokable_factory: { class: FooBarClass, factory: '@factory' }
|
||||||
|
@ -34,6 +34,7 @@ services:
|
|||||||
new_factory2: { class: FooBarClass, factory: ['@baz', getClass]}
|
new_factory2: { class: FooBarClass, factory: ['@baz', getClass]}
|
||||||
new_factory3: { class: FooBarClass, factory: [BazClass, getInstance]}
|
new_factory3: { class: FooBarClass, factory: [BazClass, getInstance]}
|
||||||
new_factory4: { class: BazClass, factory: [~, getInstance]}
|
new_factory4: { class: BazClass, factory: [~, getInstance]}
|
||||||
|
new_factory5: { class: FooBarClass, factory: '@baz' }
|
||||||
Acme\WithShortCutArgs: [foo, '@baz']
|
Acme\WithShortCutArgs: [foo, '@baz']
|
||||||
alias_for_foo: '@foo'
|
alias_for_foo: '@foo'
|
||||||
another_alias_for_foo:
|
another_alias_for_foo:
|
||||||
|
@ -268,6 +268,7 @@ class XmlFileLoaderTest extends TestCase
|
|||||||
$this->assertEquals([new Reference('baz'), 'getClass'], $services['new_factory2']->getFactory(), '->load() parses the factory tag');
|
$this->assertEquals([new Reference('baz'), 'getClass'], $services['new_factory2']->getFactory(), '->load() parses the factory tag');
|
||||||
$this->assertEquals(['BazClass', 'getInstance'], $services['new_factory3']->getFactory(), '->load() parses the factory tag');
|
$this->assertEquals(['BazClass', 'getInstance'], $services['new_factory3']->getFactory(), '->load() parses the factory tag');
|
||||||
$this->assertSame([null, 'getInstance'], $services['new_factory4']->getFactory(), '->load() accepts factory tag without class');
|
$this->assertSame([null, 'getInstance'], $services['new_factory4']->getFactory(), '->load() accepts factory tag without class');
|
||||||
|
$this->assertEquals([new Reference('baz'), '__invoke'], $services['new_factory5']->getFactory(), '->load() accepts service reference as invokable factory');
|
||||||
|
|
||||||
$aliases = $container->getAliases();
|
$aliases = $container->getAliases();
|
||||||
$this->assertArrayHasKey('alias_for_foo', $aliases, '->load() parses <service> elements');
|
$this->assertArrayHasKey('alias_for_foo', $aliases, '->load() parses <service> elements');
|
||||||
|
@ -20,6 +20,7 @@ use Symfony\Component\DependencyInjection\Argument\BoundArgument;
|
|||||||
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
|
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
|
||||||
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
|
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||||
use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
|
use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
|
||||||
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
|
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
|
||||||
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
||||||
@ -158,6 +159,7 @@ class YamlFileLoaderTest extends TestCase
|
|||||||
$this->assertEquals([new Reference('baz'), 'getClass'], $services['new_factory2']->getFactory(), '->load() parses the factory tag');
|
$this->assertEquals([new Reference('baz'), 'getClass'], $services['new_factory2']->getFactory(), '->load() parses the factory tag');
|
||||||
$this->assertEquals(['BazClass', 'getInstance'], $services['new_factory3']->getFactory(), '->load() parses the factory tag');
|
$this->assertEquals(['BazClass', 'getInstance'], $services['new_factory3']->getFactory(), '->load() parses the factory tag');
|
||||||
$this->assertSame([null, 'getInstance'], $services['new_factory4']->getFactory(), '->load() accepts factory tag without class');
|
$this->assertSame([null, 'getInstance'], $services['new_factory4']->getFactory(), '->load() accepts factory tag without class');
|
||||||
|
$this->assertEquals([new Reference('baz'), '__invoke'], $services['new_factory5']->getFactory(), '->load() accepts service reference as invokable factory');
|
||||||
$this->assertEquals(['foo', new Reference('baz')], $services['Acme\WithShortCutArgs']->getArguments(), '->load() parses short service definition');
|
$this->assertEquals(['foo', new Reference('baz')], $services['Acme\WithShortCutArgs']->getArguments(), '->load() parses short service definition');
|
||||||
|
|
||||||
$aliases = $container->getAliases();
|
$aliases = $container->getAliases();
|
||||||
@ -196,6 +198,16 @@ class YamlFileLoaderTest extends TestCase
|
|||||||
|
|
||||||
$this->assertEquals([new Reference('baz'), 'getClass'], $services['factory']->getFactory(), '->load() parses the factory tag with service:method');
|
$this->assertEquals([new Reference('baz'), 'getClass'], $services['factory']->getFactory(), '->load() parses the factory tag with service:method');
|
||||||
$this->assertEquals(['FooBacFactory', 'createFooBar'], $services['factory_with_static_call']->getFactory(), '->load() parses the factory tag with Class::method');
|
$this->assertEquals(['FooBacFactory', 'createFooBar'], $services['factory_with_static_call']->getFactory(), '->load() parses the factory tag with Class::method');
|
||||||
|
$this->assertEquals([new Reference('factory'), '__invoke'], $services['invokable_factory']->getFactory(), '->load() parses string service reference');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFactorySyntaxError()
|
||||||
|
{
|
||||||
|
$container = new ContainerBuilder();
|
||||||
|
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
|
||||||
|
$this->expectException(InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionMessage('The value of the "factory" option for the "invalid_factory" service must be the id of the service without the "@" prefix (replace "@factory:method" with "factory:method").');
|
||||||
|
$loader->load('bad_factory_syntax.yml');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLoadConfiguratorShortSyntax()
|
public function testLoadConfiguratorShortSyntax()
|
||||||
|
Reference in New Issue
Block a user