diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php
index e244bf185782..1d091a097f2b 100644
--- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php
+++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php
@@ -941,7 +941,19 @@ public function createService(Definition $definition, $id, $tryProxy = true)
$arguments = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())));
- if (null !== $definition->getFactoryMethod()) {
+ if (null !== $definition->getFactory()) {
+ $factory = $definition->getFactory();
+
+ if (is_string($factory)) {
+ $callable = $definition->getFactory();
+ } elseif (is_array($factory)) {
+ $callable = array($this->resolveServices($factory[0]), $factory[1]);
+ } else {
+ throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory', $id));
+ }
+
+ $service = call_user_func_array($callable, $arguments);
+ } elseif (null !== $definition->getFactoryMethod()) {
if (null !== $definition->getFactoryClass()) {
$factory = $parameterBag->resolveValue($definition->getFactoryClass());
} elseif (null !== $definition->getFactoryService()) {
diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php
index 920fd3bd5766..b77a13737a5b 100644
--- a/src/Symfony/Component/DependencyInjection/Definition.php
+++ b/src/Symfony/Component/DependencyInjection/Definition.php
@@ -66,6 +66,10 @@ public function __construct($class = null, array $arguments = array())
*/
public function setFactory($factory)
{
+ if (is_string($factory) && strpos($factory, '::') !== false) {
+ $factory = explode('::', $factory, 2);
+ }
+
$this->factory = $factory;
return $this;
diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
index 71732b12bfec..ba90bf514e86 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
@@ -730,13 +730,13 @@ private function addNewInstance($id, Definition $definition, $return, $instantia
$class = $this->dumpValue($callable[0]);
// If the class is a string we can optimize call_user_func away
if (strpos($class, "'") === 0) {
- return sprintf(" $return{$instantiation}\%s::%s(%s);\n", substr($class, 1, -1), $callable[1], $arguments ? implode(', ', $arguments) : '');
+ return sprintf(" $return{$instantiation}%s::%s(%s);\n", $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : '');
}
- return sprintf(" $return{$instantiation}call_user_func(array(%s, '%s'), %s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? ', '.implode(', ', $arguments) : '');
+ return sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? ', '.implode(', ', $arguments) : '');
}
- return sprintf(" $return{$instantiation}%s(%s);\n", $callable, $arguments ? implode(', ', $arguments) : '');
+ return sprintf(" $return{$instantiation}\\%s(%s);\n", $callable, $arguments ? implode(', ', $arguments) : '');
} elseif (null !== $definition->getFactoryMethod()) {
if (null !== $definition->getFactoryClass()) {
$class = $this->dumpValue($definition->getFactoryClass());
@@ -1212,7 +1212,7 @@ private function hasReference($id, array $arguments, $deep = false, array $visit
/**
* Dumps values.
*
- * @param array $value
+ * @param mixed $value
* @param bool $interpolate
*
* @return string
@@ -1249,6 +1249,30 @@ private function dumpValue($value, $interpolate = true)
throw new RuntimeException('Cannot dump definitions which have a variable class name.');
}
+ if (null !== $value->getFactory()) {
+ $factory = $value->getFactory();
+
+ if (is_string($factory)) {
+ return sprintf('\\%s(%s)', $factory, implode(', ', $arguments));
+ }
+
+ if (is_array($factory)) {
+ if (is_string($factory[0])) {
+ return sprintf('\\%s::%s(%s)', $factory[0], $factory[1], implode(', ', $arguments));
+ }
+
+ if ($factory[0] instanceof Definition) {
+ return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($factory[0]), $factory[1], count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
+ }
+
+ if ($factory[0] instanceof Reference) {
+ return sprintf('%s->%s(%s)', $this->dumpValue($factory[0]), $factory[1], implode(', ', $arguments));
+ }
+ }
+
+ throw new RuntimeException('Cannot dump definition because of invalid factory');
+ }
+
if (null !== $value->getFactoryMethod()) {
if (null !== $value->getFactoryClass()) {
return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($value->getFactoryClass()), $value->getFactoryMethod(), count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
index 202ac138edf3..bb53f8be1387 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
@@ -196,7 +196,7 @@ private function parseDefinition($id, $service, $file)
if (isset($service['factory'])) {
if (is_string($service['factory'])) {
- if (strpos($service['factory'], ':')) {
+ if (strpos($service['factory'], ':') !== false && strpos($service['factory'], '::') === false) {
$parts = explode(':', $service['factory']);
$definition->setFactory(array($this->resolveServices('@'.$parts[0]), $parts[1]));
} else {
diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
index 714a07b76818..6e839653aa02 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
@@ -341,6 +341,23 @@ public function testCreateServiceFactoryService()
$this->assertInstanceOf('BazClass', $builder->get('baz_service'));
}
+ /**
+ * @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService
+ */
+ public function testCreateServiceFactory()
+ {
+ $builder = new ContainerBuilder();
+ $builder->register('foo', 'Bar\FooClass')->setFactory('Bar\FooClass::getInstance');
+ $builder->register('qux', 'Bar\FooClass')->setFactory(array('Bar\FooClass', 'getInstance'));
+ $builder->register('bar', 'Bar\FooClass')->setFactory(array(new Definition('Bar\FooClass'), 'getInstance'));
+ $builder->register('baz', 'Bar\FooClass')->setFactory(array(new Reference('bar'), 'getInstance'));
+
+ $this->assertTrue($builder->get('foo')->called, '->createService() calls the factory method to create the service instance');
+ $this->assertTrue($builder->get('qux')->called, '->createService() calls the factory method to create the service instance');
+ $this->assertTrue($builder->get('bar')->called, '->createService() uses anonymous service as factory');
+ $this->assertTrue($builder->get('baz')->called, '->createService() uses another service as factory');
+ }
+
/**
* @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService
*/
diff --git a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php
index dfe8a03bf6aa..cfcb5f486f0c 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php
@@ -34,8 +34,12 @@ public function testConstructor()
public function testSetGetFactory()
{
$def = new Definition('stdClass');
+
$this->assertSame($def, $def->setFactory('foo'), '->setFactory() implements a fluent interface');
$this->assertEquals('foo', $def->getFactory(), '->getFactory() returns the factory');
+
+ $def->setFactory('Foo::bar');
+ $this->assertEquals(array('Foo', 'bar'), $def->getFactory(), '->setFactory() converts string static method call to the array');
}
public function testSetGetFactoryClass()
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
index 231ab773460a..6cb04446e093 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
@@ -120,6 +120,14 @@ public function testAddService()
}
}
+ public function testServicesWithAnonymousFactories()
+ {
+ $container = include self::$fixturesPath.'/containers/container19.php';
+ $dumper = new PhpDumper($container);
+
+ $this->assertStringEqualsFile(self::$fixturesPath.'/php/services19.php', $dumper->dump(), '->dump() dumps services with anonymous factories');
+ }
+
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Service id "bar$" cannot be converted to a valid PHP method name.
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container19.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container19.php
new file mode 100644
index 000000000000..64b8f066dca1
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container19.php
@@ -0,0 +1,22 @@
+register('service_from_anonymous_factory', 'Bar\FooClass')
+ ->setFactory(array(new Definition('Bar\FooClass'), 'getInstance'))
+;
+
+$anonymousServiceWithFactory = new Definition('Bar\FooClass');
+$anonymousServiceWithFactory->setFactory('Bar\FooClass::getInstance');
+$container
+ ->register('service_with_method_call_and_factory', 'Bar\FooClass')
+ ->addMethodCall('setBar', array($anonymousServiceWithFactory))
+;
+
+return $container;
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php
index aca2d012056a..49262e86326e 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php
@@ -114,5 +114,9 @@
->setProperty('foo', 'bar')
->setFactory(array(new Reference('new_factory'), 'getInstance'))
;
+$container
+ ->register('service_from_static_method', 'Bar\FooClass')
+ ->setFactory(array('Bar\FooClass', 'getInstance'))
+;
return $container;
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot
index f4f498df2ada..e233d62594d0 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot
@@ -21,6 +21,7 @@ digraph sc {
node_decorator_service_with_name [label="decorator_service_with_name\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_new_factory [label="new_factory\nFactoryClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_new_factory_service [label="new_factory_service\nFooBarBaz\n", shape=record, fillcolor="#eeeeee", style="filled"];
+ node_service_from_static_method [label="service_from_static_method\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"];
node_foo2 [label="foo2\n\n", shape=record, fillcolor="#ff9999", style="filled"];
node_foo3 [label="foo3\n\n", shape=record, fillcolor="#ff9999", style="filled"];
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php
new file mode 100644
index 000000000000..746b3fa7765f
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php
@@ -0,0 +1,64 @@
+methodMap = array(
+ 'service_from_anonymous_factory' => 'getServiceFromAnonymousFactoryService',
+ 'service_with_method_call_and_factory' => 'getServiceWithMethodCallAndFactoryService',
+ );
+ }
+
+ /**
+ * Gets the 'service_from_anonymous_factory' service.
+ *
+ * This service is shared.
+ * This method always returns the same instance of the service.
+ *
+ * @return \Bar\FooClass A Bar\FooClass instance.
+ */
+ protected function getServiceFromAnonymousFactoryService()
+ {
+ return $this->services['service_from_anonymous_factory'] = call_user_func(array(new \Bar\FooClass(), 'getInstance'));
+ }
+
+ /**
+ * Gets the 'service_with_method_call_and_factory' service.
+ *
+ * This service is shared.
+ * This method always returns the same instance of the service.
+ *
+ * @return \Bar\FooClass A Bar\FooClass instance.
+ */
+ protected function getServiceWithMethodCallAndFactoryService()
+ {
+ $this->services['service_with_method_call_and_factory'] = $instance = new \Bar\FooClass();
+
+ $instance->setBar(\Bar\FooClass::getInstance());
+
+ return $instance;
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php
index 78eb1d8ee8ef..3cc3aac8b845 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php
@@ -47,6 +47,7 @@ public function __construct()
'new_factory' => 'getNewFactoryService',
'new_factory_service' => 'getNewFactoryServiceService',
'request' => 'getRequestService',
+ 'service_from_static_method' => 'getServiceFromStaticMethodService',
);
$this->aliases = array(
'alias_for_alias' => 'foo',
@@ -303,6 +304,19 @@ protected function getRequestService()
throw new RuntimeException('You have requested a synthetic service ("request"). The DIC does not know how to construct this service.');
}
+ /**
+ * Gets the 'service_from_static_method' service.
+ *
+ * This service is shared.
+ * This method always returns the same instance of the service.
+ *
+ * @return \Bar\FooClass A Bar\FooClass instance.
+ */
+ protected function getServiceFromStaticMethodService()
+ {
+ return $this->services['service_from_static_method'] = \Bar\FooClass::getInstance();
+ }
+
/**
* Updates the 'request' service.
*/
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php
index 9f25dc289b32..5e8103bdd0cc 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php
@@ -50,6 +50,7 @@ public function __construct()
'method_call1' => 'getMethodCall1Service',
'new_factory_service' => 'getNewFactoryServiceService',
'request' => 'getRequestService',
+ 'service_from_static_method' => 'getServiceFromStaticMethodService',
);
$this->aliases = array(
'alias_for_alias' => 'foo',
@@ -305,6 +306,19 @@ protected function getRequestService()
throw new RuntimeException('You have requested a synthetic service ("request"). The DIC does not know how to construct this service.');
}
+ /**
+ * Gets the 'service_from_static_method' service.
+ *
+ * This service is shared.
+ * This method always returns the same instance of the service.
+ *
+ * @return \Bar\FooClass A Bar\FooClass instance.
+ */
+ protected function getServiceFromStaticMethodService()
+ {
+ return $this->services['service_from_static_method'] = \Bar\FooClass::getInstance();
+ }
+
/**
* Updates the 'request' service.
*/
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml
index 459563bb0e1e..7234a82cdc4f 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml
@@ -98,6 +98,9 @@
bar
+
+
+
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services14.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services14.yml
index a6f825e42e31..4c188c5fbc7b 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services14.yml
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services14.yml
@@ -1,2 +1,3 @@
services:
factory: { class: FooBarClass, factory: baz:getClass}
+ factory_with_static_call: { class: FooBarClass, factory: FooBacFactory::createFooBar}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml
index e782ca5165ef..0b8da4396878 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml
@@ -96,5 +96,8 @@ services:
class: FooBarBaz
properties: { foo: bar }
factory: ['@new_factory', getInstance]
+ service_from_static_method:
+ class: Bar\FooClass
+ factory: [Bar\FooClass, getInstance]
alias_for_foo: @foo
alias_for_alias: @foo
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
index 1bba6a1ac830..3de0843dfbdf 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
@@ -169,7 +169,8 @@ public function testLoadFactoryShortSyntax()
$loader->load('services14.yml');
$services = $container->getDefinitions();
- $this->assertEquals(array(new Reference('baz'), 'getClass'), $services['factory']->getFactory(), '->load() parses the factory tag');
+ $this->assertEquals(array(new Reference('baz'), 'getClass'), $services['factory']->getFactory(), '->load() parses the factory tag with service:method');
+ $this->assertEquals(array('FooBacFactory', 'createFooBar'), $services['factory_with_static_call']->getFactory(), '->load() parses the factory tag with Class::method');
}
public function testExtensions()