Browse Source

[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 năm trước cách đây
mục cha
commit
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');