Skip to content

Commit

Permalink
[DependencyInjection] Add factory-class and factory-service concepts …
Browse files Browse the repository at this point in the history
…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!
  • Loading branch information
beberlei authored and fabpot committed Jul 5, 2010
1 parent 27458b6 commit ef91396
Show file tree
Hide file tree
Showing 14 changed files with 141 additions and 5 deletions.
9 changes: 8 additions & 1 deletion src/Symfony/Components/DependencyInjection/Builder.php
Expand Up @@ -334,7 +334,14 @@ protected function createService(Definition $definition, $id)
$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);
}
Expand Down
45 changes: 44 additions & 1 deletion src/Symfony/Components/DependencyInjection/Definition.php
Expand Up @@ -23,6 +23,8 @@ class Definition
protected $class;
protected $file;
protected $factoryMethod;
protected $factoryService;
protected $factoryClass;
protected $shared;
protected $arguments;
protected $calls;
Expand Down Expand Up @@ -61,13 +63,54 @@ public function setFactoryMethod($method)
/**
* 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.
*
Expand Down
Expand Up @@ -93,7 +93,13 @@ protected function addServiceInstance($id, $definition)
}

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 {
Expand Down
Expand Up @@ -47,10 +47,12 @@ protected function addParameters()

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"' : ''
);

Expand Down
Expand Up @@ -66,6 +66,14 @@ protected function addService($id, $definition)
$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));
}
Expand Down
Expand Up @@ -134,7 +134,7 @@ protected function parseDefinition(BuilderConfiguration $configuration, $id, $se

$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));
Expand Down
Expand Up @@ -141,6 +141,14 @@ protected function parseDefinition(BuilderConfiguration $configuration, $id, $se
$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']);
}
Expand Down
23 changes: 23 additions & 0 deletions tests/Symfony/Tests/Components/DependencyInjection/BuilderTest.php
Expand Up @@ -394,4 +394,27 @@ public function testFindAnnotatedServiceIds()
), '->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'));
}
}
Expand Up @@ -37,6 +37,21 @@ public function testSetGetConstructor()
$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
Expand Down
Expand Up @@ -30,3 +30,16 @@ static public function configureStatic1()
{
}
}

class BazFactory
{
static public function createStatic()
{
return new BazClass();
}

public function create()
{
return new BazClass();
}
}
Expand Up @@ -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>
Expand Up @@ -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 }
Expand Up @@ -127,6 +127,9 @@ public function testLoadServices()
$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');
Expand Down
Expand Up @@ -94,6 +94,9 @@ public function testLoadServices()
$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');
Expand Down

0 comments on commit ef91396

Please sign in to comment.