8000 [DependencyInjection] Invokable Factory Services by zanbaldwin · Pull Request #30255 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[DependencyInjection] Invokable Factory Services #30255

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/Symfony/Component/DependencyInjection/Definition.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public function setChanges(array $changes)
/**
* 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
*/
Expand All @@ -105,6 +105,8 @@ public function setFactory($factory)

if (\is_string($factory) && false !== strpos($factory, '::')) {
$factory = explode('::', $factory, 2);
} elseif ($factory instanceof Reference) {
$factory = [$factory, '__invoke'];
}

$this->factory = $factory;
Expand Down Expand Up @@ -782,7 +784,7 @@ public function getDeprecationMessage($id)
/**
* 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
*/
Expand All @@ -792,6 +794,8 @@ public function setConfigurator($configurator)

if (\is_string($configurator) && false !== strpos($configurator, '::')) {
$configurator = explode('::', $configurator, 2);
} elseif ($configurator instanceof Reference) {
$configurator = [$configurator, '__invoke'];
}

$this->configurator = $configurator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ private function parseDefinition(\DOMElement $service, $file, array $defaults)
$class = $factory->hasAttribute('class') ? $factory->getAttribute('class') : null;
}

$definition->setFactory([$class, $factory->getAttribute('method')]);
$definition->setFactory([$class, $factory->getAttribute('method') ?: '__invoke']);
}
}

Expand All @@ -332,7 +332,7 @@ private function parseDefinition(\DOMElement $service, $file, array $defaults)
$class = $configurator->getAttribute('class');
}

$definition->setConfigurator([$class, $configurator->getAttribute('method')]);
$definition->setConfigurator([$class, $configurator->getAttribute('method') ?: '__invoke']);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -571,12 +571,15 @@ private function parseDefinition($id, $service, $file, array $defaults)
*
* @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)
{
if (\is_string($callable)) {
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)));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

class DefinitionTest extends TestCase
{
Expand All @@ -35,6 +36,9 @@ public function testSetGetFactory()

$def->setFactory('Foo::bar');
$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());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
<service id="new_factory4" class="BazClass">
<factory method="getInstance" />
</service>
<service id="new_factory5" class="FooBarClass">
<factory service="baz" />
</service>
<service id="alias_for_foo" alias="foo" />
<service id="another_alias_for_foo" alias="foo" public="false" />
<service id="0" class="FooClass" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
services:
factory:
class: Baz
invalid_factory:
class: FooBarClass
factory: '@factory:method'
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
services:
factory: { class: FooBarClass, factory: baz:getClass}
factory_with_static_call: { class: FooBarClass, factory: FooBacFactory::createFooBar}
invokable_factory: { class: FooBarClass, factory: '@factory' }
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ services:
new_factory2: { class: FooBarClass, factory: ['@baz', getClass]}
new_factory3: { class: FooBarClass, factory: [BazClass, getInstance]}
new_factory4: { class: BazClass, factory: [~, getInstance]}
new_factory5: { class: FooBarClass, factory: '@baz' }
Acme\WithShortCutArgs: [foo, '@baz']
alias_for_foo: '@foo'
another_alias_for_foo:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ public function testLoadServices()
$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->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();
$this->assertArrayHasKey('alias_for_foo', $aliases, '->load() parses <service> elements');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
Expand Down Expand Up @@ -158,6 +159,7 @@ public function testLoadServices()
$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->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');

$aliases = $container->getAliases();
Expand Down Expand Up @@ -196,6 +198,16 @@ public function testLoadFactoryShortSyntax()

$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([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()
Expand Down
0