瀏覽代碼

[DependencyInjection] Add factory-class and factory-service concepts to DI Definition. A factory-class is a class-name for the static creation method of a service. A factory-service is the service object that has a factory method to construct a given service. Added tests. Changed Xml, Yaml Dumper and Loaders, PHP Dumper, aswell as the Runtime Builder Container. Graphviz changes missing!

Benjamin Eberlei 15 年之前
父節點
當前提交
ef91396618

+ 8 - 1
src/Symfony/Components/DependencyInjection/Builder.php

@@ -334,7 +334,14 @@ class Builder extends Container implements AnnotatedContainerInterface
         $arguments = $this->resolveServices(self::resolveValue($definition->getArguments(), $this->getParameterBag()->all()));
 
         if (null !== $definition->getFactoryMethod()) {
-            $service = call_user_func_array(array(self::resolveValue($definition->getClass(), $this->getParameterBag()->all()), $definition->getFactoryMethod()), $arguments);
+            if (null !== $definition->getFactoryService()) {
+                $factoryService = $this->get(self::resolveValue($definition->getFactoryService(), $this->getParameterBag()->all()));
+                $service = call_user_func_array(array($factoryService, $definition->getFactoryMethod()), $arguments);
+            } else if(null !== $definition->getFactoryClass()) {
+                $service = call_user_func_array(array(self::resolveValue($definition->getFactoryClass(), $this->getParameterBag()->all()), $definition->getFactoryMethod()), $arguments);
+            } else {
+                $service = call_user_func_array(array(self::resolveValue($definition->getClass(), $this->getParameterBag()->all()), $definition->getFactoryMethod()), $arguments);
+            }
         } else {
             $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments);
         }

+ 44 - 1
src/Symfony/Components/DependencyInjection/Definition.php

@@ -23,6 +23,8 @@ class Definition
     protected $class;
     protected $file;
     protected $factoryMethod;
+    protected $factoryService;
+    protected $factoryClass;
     protected $shared;
     protected $arguments;
     protected $calls;
@@ -61,13 +63,54 @@ class Definition
     /**
      * Gets the factory method.
      *
-     * @return Definition The factory method name
+     * @return string The factory method name
      */
     public function getFactoryMethod()
     {
         return $this->factoryMethod;
     }
 
+    /**
+     * Set the name of the service that acts as a factory using the specified `constructor` method.
+     *
+     * @param string
+     */
+    public function setFactoryService($factoryService)
+    {
+        $this->factoryService = $factoryService;
+        return $this;
+    }
+
+    /**
+     * @return string
+     */
+    public function getFactoryService()
+    {
+        return $this->factoryService;
+    }
+
+    /**
+     * If service has a constructor method but no factory service, this class is the static callback.
+     *
+     * @param  string $factoryClass
+     * @return Definition
+     */
+    public function setFactoryClass($factoryClass)
+    {
+        $this->factoryClass = $factoryClass;
+        return $this;
+    }
+
+    /**
+     * Get the current static create class for this service.
+     *
+     * @return string
+     */
+    public function getFactoryClass()
+    {
+        return $this->factoryClass;
+    }
+
     /**
      * Sets the service class.
      *

+ 7 - 1
src/Symfony/Components/DependencyInjection/Dumper/PhpDumper.php

@@ -93,7 +93,13 @@ EOF;
         }
 
         if (null !== $definition->getFactoryMethod()) {
-            $code = sprintf("        \$instance = call_user_func(array(%s, '%s')%s);\n", $class, $definition->getFactoryMethod(), $arguments ? ', '.implode(', ', $arguments) : '');
+            if (null !== $definition->getFactoryService()) {
+                $code = sprintf("        \$instance = \$this->get%sService()->%s(%s);\n", $this->dumpValue($definition->getFactoryService()), $definition->getFactoryMethod(), implode(', ', $arguments));
+            } elseif (null !== $definition->getFactoryClass()) {
+                $code = sprintf("        \$instance = call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($definition->getFactoryClass()), $definition->getFactoryMethod(), $arguments ? ', '.implode(', ', $arguments) : '');
+            } else {
+                $code = sprintf("        \$instance = call_user_func(array(%s, '%s')%s);\n", $class, $definition->getFactoryMethod(), $arguments ? ', '.implode(', ', $arguments) : '');
+            }
         } elseif ($class != "'".str_replace('\\', '\\\\', $definition->getClass())."'") {
             $code = sprintf("        \$class = %s;\n        \$instance = new \$class(%s);\n", $class, implode(', ', $arguments));
         } else {

+ 3 - 1
src/Symfony/Components/DependencyInjection/Dumper/XmlDumper.php

@@ -47,10 +47,12 @@ class XmlDumper extends Dumper
 
     protected function addService($id, $definition)
     {
-        $code = sprintf("    <service id=\"%s\" class=\"%s\"%s%s>\n",
+        $code = sprintf("    <service id=\"%s\" class=\"%s\"%s%s%s%s>\n",
             $id,
             $definition->getClass(),
             $definition->getFactoryMethod() ? sprintf(' factory-method="%s"', $definition->getFactoryMethod()) : '',
+            $definition->getFactoryClass() ? sprintf(' factoryclass="%s"', $definition->getFactoryClass()) : '',
+            $definition->getFactoryService() ? sprintf(' constructor="%s"', $definition->getFactoryService()) : '',
             !$definition->isShared() ? ' shared="false"' : ''
         );
 

+ 8 - 0
src/Symfony/Components/DependencyInjection/Dumper/YamlDumper.php

@@ -66,6 +66,14 @@ class YamlDumper extends Dumper
             $code .= sprintf("    factory_method: %s\n", $definition->getFactoryMethod());
         }
 
+        if ($definition->getFactoryClass()) {
+            $code .= sprintf("    factoryClass: %s\n", $definition->getFactoryClass());
+        }
+
+        if ($definition->getFactoryService()) {
+            $code .= sprintf("    factoryService: %s\n", $definition->getFactoryService());
+        }
+
         if ($definition->getArguments()) {
             $code .= sprintf("    arguments: %s\n", Yaml::dump($this->dumpValue($definition->getArguments()), 0));
         }

+ 1 - 1
src/Symfony/Components/DependencyInjection/Loader/XmlFileLoader.php

@@ -134,7 +134,7 @@ class XmlFileLoader extends FileLoader
 
         $definition = new Definition((string) $service['class']);
 
-        foreach (array('shared', 'factory-method') as $key) {
+        foreach (array('shared', 'factory-method', 'factoryservice', 'factoryclass') as $key) {
             if (isset($service[$key])) {
                 $method = 'set'.str_replace('-', '', $key);
                 $definition->$method((string) $service->getAttributeAsPhp($key));

+ 8 - 0
src/Symfony/Components/DependencyInjection/Loader/YamlFileLoader.php

@@ -141,6 +141,14 @@ class YamlFileLoader extends FileLoader
             $definition->setFactoryMethod($service['factory_method']);
         }
 
+        if (isset($service['factoryClass'])) {
+            $definition->setFactoryClass($service['factoryClass']);
+        }
+
+        if (isset($service['factoryService'])) {
+            $definition->setFactoryService($service['factoryService']);
+        }
+
         if (isset($service['file'])) {
             $definition->setFile($service['file']);
         }

+ 23 - 0
tests/Symfony/Tests/Components/DependencyInjection/BuilderTest.php

@@ -394,4 +394,27 @@ class BuilderTest extends \PHPUnit_Framework_TestCase
         ), '->findAnnotatedServiceIds() returns an array of service ids and its annotation attributes');
         $this->assertEquals(array(), $builder->findAnnotatedServiceIds('foobar'), '->findAnnotatedServiceIds() returns an empty array if there is annotated services');
     }
+
+    public function testFactories()
+    {
+        $def1 = new Definition('BazClass');
+        $def1->setFactoryClass('BazFactory');
+        $def1->setConstructor('createStatic');
+
+        $def2 = new Definition('BazClass');
+        $def2->setFactoryService('BazFactoryService');
+        $def2->setFactoryMethod('create');
+
+        $def3 = new Definition('BazFactory');
+
+        $builder = new Builder();
+        $builder->addDefinitions(array(
+            'baz_factory' => $def1,
+            'baz_service' => $def2,
+            'BazFactoryService' => $def3,
+        ));
+
+        $this->assertType('BazClass', $builder->get('baz_factory'));
+        $this->assertType('Bazclass', $builder->get('baz_service'));
+    }
 }

+ 15 - 0
tests/Symfony/Tests/Components/DependencyInjection/DefinitionTest.php

@@ -37,6 +37,21 @@ class DefinitionTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals('foo', $def->getFactoryMethod(), '->getFactoryMethod() returns the factory method name');
     }
 
+    public function testSetGetFactoryClass()
+    {
+        $def = new Definition('stdClass');
+        $this->assertSame($def, $def->setFactoryClass('stdClass2'), "->setFactoryClass() implements a fluent interface.");
+        $this->assertEquals('stdClass2', $def->getFactoryClass(), 'Overwrite default factory class method did not work.');
+    }
+
+    public function testSetGetFactoryService()
+    {
+        $def = new Definition('stdClass');
+        $this->assertNull($def->getFactoryService());
+        $this->assertSame($def, $def->setFactoryService('stdClass2'), "->setFactoryService() implements a fluent interface.");
+        $this->assertEquals('stdClass2', $def->getFactoryService(), "->getFactoryService() returns current service to construct this service.");
+    }
+
     /**
      * @covers Symfony\Components\DependencyInjection\Definition::setClass
      * @covers Symfony\Components\DependencyInjection\Definition::getClass

+ 13 - 0
tests/Symfony/Tests/Components/DependencyInjection/Fixtures/includes/classes.php

@@ -30,3 +30,16 @@ class BazClass
     {
     }
 }
+
+class BazFactory
+{
+    static public function createStatic()
+    {
+        return new BazClass();
+    }
+
+    public function create()
+    {
+        return new BazClass();
+    }
+}

+ 3 - 0
tests/Symfony/Tests/Components/DependencyInjection/Fixtures/xml/services6.xml

@@ -43,5 +43,8 @@
       </call>
     </service>
     <service id="alias_for_foo" alias="foo" />
+
+    <service id="factory_class" constructor="createStatic" factoryclass="BazFactory" />
+    <service id="factory_service" constructor="create" factoryservice="BazFactoryService" />
   </services>
 </container>

+ 2 - 0
tests/Symfony/Tests/Components/DependencyInjection/Fixtures/yaml/services6.yml

@@ -18,3 +18,5 @@ services:
     calls:
       - [ setBar, [ foo, @foo, [true, false] ] ]
   alias_for_foo: @foo
+  factory_class: { class: BazClass, factoryClass: BazFactory }
+  factory_service: { class: BazClass, factoryService: BazFactoryService }

+ 3 - 0
tests/Symfony/Tests/Components/DependencyInjection/Loader/XmlFileLoaderTest.php

@@ -127,6 +127,9 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals(array('BazClass', 'configureStatic'), $services['configurator3']->getConfigurator(), '->load() parses the configurator tag');
         $this->assertEquals(array(array('setBar', array())), $services['method_call1']->getMethodCalls(), '->load() parses the method_call tag');
         $this->assertEquals(array(array('setBar', array('foo', new Reference('foo'), array(true, false)))), $services['method_call2']->getMethodCalls(), '->load() parses the method_call tag');
+        $this->assertEquals('BazFactory', $services['factory_class']->getFactoryClass());
+        $this->assertEquals('BazFactoryService', $services['factory_service']->getFactoryService());
+
         $aliases = $config->getAliases();
         $this->assertTrue(isset($aliases['alias_for_foo']), '->load() parses <service> elements');
         $this->assertEquals('foo', $aliases['alias_for_foo'], '->load() parses aliases');

+ 3 - 0
tests/Symfony/Tests/Components/DependencyInjection/Loader/YamlFileLoaderTest.php

@@ -94,6 +94,9 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals(array('BazClass', 'configureStatic'), $services['configurator3']->getConfigurator(), '->load() parses the configurator tag');
         $this->assertEquals(array(array('setBar', array())), $services['method_call1']->getMethodCalls(), '->load() parses the method_call tag');
         $this->assertEquals(array(array('setBar', array('foo', new Reference('foo'), array(true, false)))), $services['method_call2']->getMethodCalls(), '->load() parses the method_call tag');
+        $this->assertEquals('BazFactory', $services['factory_class']->getFactoryClass());
+        $this->assertEquals('BazFactoryService', $services['factory_service']->getFactoryService());
+        
         $aliases = $config->getAliases();
         $this->assertTrue(isset($aliases['alias_for_foo']), '->load() parses aliases');
         $this->assertEquals('foo', $aliases['alias_for_foo'], '->load() parses aliases');