From aee57315c56665a732e510b608c0ed2e34da16d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Mon, 24 Aug 2015 03:36:41 +0200 Subject: [PATCH] [DependencyInjection] Add autowiring capabilities --- .../Compiler/AutowirePass.php | 263 +++++++++++++++ .../Compiler/PassConfig.php | 1 + .../DependencyInjection/Definition.php | 94 ++++++ .../Loader/XmlFileLoader.php | 8 + .../Loader/YamlFileLoader.php | 22 ++ .../schema/dic/services/services-1.0.xsd | 2 + .../Tests/Compiler/AutowirePassTest.php | 300 ++++++++++++++++++ .../Tests/ContainerBuilderTest.php | 24 ++ .../Tests/DefinitionTest.php | 21 ++ .../Tests/Fixtures/xml/services22.xml | 9 + .../Tests/Fixtures/xml/services23.xml | 6 + .../Tests/Fixtures/yaml/bad_types1.yml | 5 + .../Tests/Fixtures/yaml/bad_types2.yml | 5 + .../Tests/Fixtures/yaml/services22.yml | 8 + .../Tests/Fixtures/yaml/services23.yml | 4 + .../Tests/Loader/XmlFileLoaderTest.php | 18 ++ .../Tests/Loader/YamlFileLoaderTest.php | 37 +++ 17 files changed, 827 insertions(+) create mode 100644 src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services22.xml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services23.xml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_types1.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_types2.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services22.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services23.yml diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php new file mode 100644 index 0000000000..3b85543b8b --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -0,0 +1,263 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Guesses constructor arguments of services definitions and try to instantiate services if necessary. + * + * @author Kévin Dunglas + */ +class AutowirePass implements CompilerPassInterface +{ + private $container; + private $reflectionClasses = array(); + private $definedTypes = array(); + private $types; + private $notGuessableTypes = array(); + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + $this->container = $container; + foreach ($container->getDefinitions() as $id => $definition) { + if ($definition->isAutowired()) { + $this->completeDefinition($id, $definition); + } + } + + // Free memory and remove circular reference to container + $this->container = null; + $this->reflectionClasses = array(); + $this->definedTypes = array(); + $this->types = null; + $this->notGuessableTypes = array(); + } + + /** + * Wires the given definition. + * + * @param string $id + * @param Definition $definition + * + * @throws RuntimeException + */ + private function completeDefinition($id, Definition $definition) + { + if (!$reflectionClass = $this->getReflectionClass($id, $definition)) { + return; + } + + $this->container->addClassResource($reflectionClass); + + if (!$constructor = $reflectionClass->getConstructor()) { + return; + } + + $arguments = $definition->getArguments(); + foreach ($constructor->getParameters() as $index => $parameter) { + $argumentExists = array_key_exists($index, $arguments); + if ($argumentExists && '' !== $arguments[$index]) { + continue; + } + + try { + if (!$typeHint = $parameter->getClass()) { + continue; + } + + if (null === $this->types) { + $this->populateAvailableTypes(); + } + + if (isset($this->types[$typeHint->name])) { + $value = new Reference($this->types[$typeHint->name]); + } else { + try { + $value = $this->createAutowiredDefinition($typeHint, $id); + } catch (RuntimeException $e) { + if (!$parameter->isDefaultValueAvailable()) { + throw $e; + } + + $value = $parameter->getDefaultValue(); + } + } + } catch (\ReflectionException $reflectionException) { + // Typehint against a non-existing class + + if (!$parameter->isDefaultValueAvailable()) { + continue; + } + + $value = $parameter->getDefaultValue(); + } + + if ($argumentExists) { + $definition->replaceArgument($index, $value); + } else { + $definition->addArgument($value); + } + } + } + + /** + * Populates the list of available types. + */ + private function populateAvailableTypes() + { + $this->types = array(); + + foreach ($this->container->getDefinitions() as $id => $definition) { + $this->populateAvailableType($id, $definition); + } + } + + /** + * Populates the list of available types for a given definition. + * + * @param string $id + * @param Definition $definition + */ + private function populateAvailableType($id, Definition $definition) + { + if (!$definition->getClass()) { + return; + } + + foreach ($definition->getAutowiringTypes() as $type) { + $this->definedTypes[$type] = true; + $this->types[$type] = $id; + } + + if ($reflectionClass = $this->getReflectionClass($id, $definition)) { + $this->extractInterfaces($id, $reflectionClass); + $this->extractAncestors($id, $reflectionClass); + } + } + + /** + * Extracts the list of all interfaces implemented by a class. + * + * @param string $id + * @param \ReflectionClass $reflectionClass + */ + private function extractInterfaces($id, \ReflectionClass $reflectionClass) + { + foreach ($reflectionClass->getInterfaces() as $interfaceName => $reflectionInterface) { + $this->set($interfaceName, $id); + + $this->extractInterfaces($id, $reflectionInterface); + } + } + + /** + * Extracts all inherited types of a class. + * + * @param string $id + * @param \ReflectionClass $reflectionClass + */ + private function extractAncestors($id, \ReflectionClass $reflectionClass) + { + $this->set($reflectionClass->name, $id); + + if ($reflectionParentClass = $reflectionClass->getParentClass()) { + $this->extractAncestors($id, $reflectionParentClass); + } + } + + /** + * Associates a type and a service id if applicable. + * + * @param string $type + * @param string $id + */ + private function set($type, $id) + { + if (isset($this->definedTypes[$type]) || isset($this->notGuessableTypes[$type])) { + return; + } + + if (isset($this->types[$type])) { + if ($this->types[$type] === $id) { + return; + } + + unset($this->types[$type]); + $this->notGuessableTypes[$type] = true; + + return; + } + + $this->types[$type] = $id; + } + + /** + * Registers a definition for the type if possible or throws an exception. + * + * @param \ReflectionClass $typeHint + * @param string $id + * + * @return Reference A reference to the registered definition + * + * @throws RuntimeException + */ + private function createAutowiredDefinition(\ReflectionClass $typeHint, $id) + { + if (!$typeHint->isInstantiable()) { + throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s".', $typeHint->name, $id)); + } + + $argumentId = sprintf('autowired.%s', $typeHint->name); + + $argumentDefinition = $this->container->register($argumentId, $typeHint->name); + $argumentDefinition->setPublic(false); + + $this->populateAvailableType($argumentId, $argumentDefinition); + $this->completeDefinition($argumentId, $argumentDefinition); + + return new Reference($argumentId); + } + + /** + * Retrieves the reflection class associated with the given service. + * + * @param string $id + * @param Definition $definition + * + * @return \ReflectionClass|null + */ + private function getReflectionClass($id, Definition $definition) + { + if (isset($this->reflectionClasses[$id])) { + return $this->reflectionClasses[$id]; + } + + if (!$class = $definition->getClass()) { + return; + } + + $class = $this->container->getParameterBag()->resolveValue($class); + + try { + return $this->reflectionClasses[$id] = new \ReflectionClass($class); + } catch (\ReflectionException $reflectionException) { + // return null + } + } +} diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php index 2849dfe129..246529d865 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -50,6 +50,7 @@ class PassConfig new CheckDefinitionValidityPass(), new ResolveReferencesToAliasesPass(), new ResolveInvalidReferencesPass(), + new AutowirePass(), new AnalyzeServiceReferencesPass(true), new CheckCircularReferencesPass(), new CheckReferenceValidityPass(), diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index 9ce81e3a12..66ea2cc0e4 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -41,6 +41,8 @@ class Definition private $synchronized = false; private $lazy = false; private $decoratedService; + private $autowired = false; + private $autowiringTypes = array(); protected $arguments; @@ -818,4 +820,96 @@ class Definition { return $this->configurator; } + + /** + * Sets types that will default to this definition. + * + * @param string[] $types + * + * @return Definition The current instance + */ + public function setAutowiringTypes(array $types) + { + $this->autowiringTypes = array(); + + foreach ($types as $type) { + $this->autowiringTypes[$type] = true; + } + + return $this; + } + + /** + * Is the definition autowired? + * + * @return bool + */ + public function isAutowired() + { + return $this->autowired; + } + + /** + * Sets autowired. + * + * @param $autowired + * + * @return Definition The current instance + */ + public function setAutowired($autowired) + { + $this->autowired = $autowired; + + return $this; + } + + /** + * Gets autowiring types that will default to this definition. + * + * @return string[] + */ + public function getAutowiringTypes() + { + return array_keys($this->autowiringTypes); + } + + /** + * Adds a type that will default to this definition. + * + * @param string $type + * + * @return Definition The current instance + */ + public function addAutowiringType($type) + { + $this->autowiringTypes[$type] = true; + + return $this; + } + + /** + * Removes a type. + * + * @param string $type + * + * @return Definition The current instance + */ + public function removeAutowiringType($type) + { + unset($this->autowiringTypes[$type]); + + return $this; + } + + /** + * Will this definition default for the given type? + * + * @param string $type + * + * @return bool + */ + public function hasAutowiringType($type) + { + return isset($this->autowiringTypes[$type]); + } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 8a0e4ac24a..7fb98972c0 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -157,6 +157,10 @@ class XmlFileLoader extends FileLoader } } + if ($value = $service->getAttribute('autowire')) { + $definition->setAutowired(XmlUtils::phpize($value)); + } + if ($value = $service->getAttribute('scope')) { $triggerDeprecation = 'request' !== (string) $service->getAttribute('id'); @@ -247,6 +251,10 @@ class XmlFileLoader extends FileLoader $definition->addTag($tag->getAttribute('name'), $parameters); } + foreach ($this->getChildren($service, 'autowiring-type') as $type) { + $definition->addAutowiringType($type->textContent); + } + if ($value = $service->getAttribute('decorates')) { $renameId = $service->hasAttribute('decoration-inner-name') ? $service->getAttribute('decoration-inner-name') : null; $priority = $service->hasAttribute('decoration-priority') ? $service->getAttribute('decoration-priority') : 0; diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 19191a760c..3039885ec2 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -299,6 +299,28 @@ class YamlFileLoader extends FileLoader $definition->setDecoratedService($service['decorates'], $renameId, $priority); } + if (isset($service['autowire'])) { + $definition->setAutowired($service['autowire']); + } + + if (isset($service['autowiring_types'])) { + if (is_string($service['autowiring_types'])) { + $definition->addAutowiringType($service['autowiring_types']); + } else { + if (!is_array($service['autowiring_types'])) { + throw new InvalidArgumentException(sprintf('Parameter "autowiring_types" must be a string or an array for service "%s" in %s. Check your YAML syntax.', $id, $file)); + } + + foreach ($service['autowiring_types'] as $autowiringType) { + if (!is_string($autowiringType)) { + throw new InvalidArgumentException(sprintf('A "autowiring_types" attribute must be of type string for service "%s" in %s. Check your YAML syntax.', $id, $file)); + } + + $definition->addAutowiringType($autowiringType); + } + } + } + $this->container->setDefinition($id, $definition); } diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd index cbaa3606f7..3241c43c40 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd +++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -85,6 +85,7 @@ + @@ -103,6 +104,7 @@ + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php new file mode 100644 index 0000000000..3639a2138f --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -0,0 +1,300 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\AutowirePass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Kévin Dunglas + */ +class AutowirePassTest extends \PHPUnit_Framework_TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + + $container->register('foo', __NAMESPACE__.'\Foo'); + $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar'); + $barDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertCount(1, $container->getDefinition('bar')->getArguments()); + $this->assertEquals('foo', (string) $container->getDefinition('bar')->getArgument(0)); + } + + public function testProcessAutowireParent() + { + $container = new ContainerBuilder(); + + $container->register('b', __NAMESPACE__.'\B'); + $cDefinition = $container->register('c', __NAMESPACE__.'\C'); + $cDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertCount(1, $container->getDefinition('c')->getArguments()); + $this->assertEquals('b', (string) $container->getDefinition('c')->getArgument(0)); + } + + public function testProcessAutowireInterface() + { + $container = new ContainerBuilder(); + + $container->register('f', __NAMESPACE__.'\F'); + $gDefinition = $container->register('g', __NAMESPACE__.'\G'); + $gDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertCount(2, $container->getDefinition('g')->getArguments()); + $this->assertEquals('f', (string) $container->getDefinition('g')->getArgument(0)); + $this->assertEquals('f', (string) $container->getDefinition('g')->getArgument(1)); + } + + public function testCompleteExistingDefinition() + { + $container = new ContainerBuilder(); + + $container->register('b', __NAMESPACE__.'\B'); + $container->register('f', __NAMESPACE__.'\F'); + $hDefinition = $container->register('h', __NAMESPACE__.'\H')->addArgument(new Reference('b')); + $hDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertCount(2, $container->getDefinition('h')->getArguments()); + $this->assertEquals('b', (string) $container->getDefinition('h')->getArgument(0)); + $this->assertEquals('f', (string) $container->getDefinition('h')->getArgument(1)); + } + + public function testCompleteExistingDefinitionWithNotDefinedArguments() + { + $container = new ContainerBuilder(); + + $container->register('b', __NAMESPACE__.'\B'); + $container->register('f', __NAMESPACE__.'\F'); + $hDefinition = $container->register('h', __NAMESPACE__.'\H')->addArgument('')->addArgument(''); + $hDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertCount(2, $container->getDefinition('h')->getArguments()); + $this->assertEquals('b', (string) $container->getDefinition('h')->getArgument(0)); + $this->assertEquals('f', (string) $container->getDefinition('h')->getArgument(1)); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a". + */ + public function testTypeCollision() + { + $container = new ContainerBuilder(); + + $container->register('c1', __NAMESPACE__.'\CollisionA'); + $container->register('c2', __NAMESPACE__.'\CollisionB'); + $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired'); + $aDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + } + + public function testWithTypeSet() + { + $container = new ContainerBuilder(); + + $container->register('c1', __NAMESPACE__.'\CollisionA'); + $container->register('c2', __NAMESPACE__.'\CollisionB')->addAutowiringType(__NAMESPACE__.'\CollisionInterface'); + $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired'); + $aDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertCount(1, $container->getDefinition('a')->getArguments()); + $this->assertEquals('c2', (string) $container->getDefinition('a')->getArgument(0)); + } + + public function testCreateDefinition() + { + $container = new ContainerBuilder(); + + $coopTilleulsDefinition = $container->register('coop_tilleuls', __NAMESPACE__.'\LesTilleuls'); + $coopTilleulsDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertCount(1, $container->getDefinition('coop_tilleuls')->getArguments()); + $this->assertEquals('autowired.symfony\component\dependencyinjection\tests\compiler\dunglas', $container->getDefinition('coop_tilleuls')->getArgument(0)); + + $dunglasDefinition = $container->getDefinition('autowired.symfony\component\dependencyinjection\tests\compiler\dunglas'); + $this->assertEquals(__NAMESPACE__.'\Dunglas', $dunglasDefinition->getClass()); + $this->assertFalse($dunglasDefinition->isPublic()); + $this->assertCount(1, $dunglasDefinition->getArguments()); + $this->assertEquals('autowired.symfony\component\dependencyinjection\tests\compiler\lille', $dunglasDefinition->getArgument(0)); + + $lilleDefinition = $container->getDefinition('autowired.symfony\component\dependencyinjection\tests\compiler\lille'); + $this->assertEquals(__NAMESPACE__.'\Lille', $lilleDefinition->getClass()); + } + + public function testResolveParameter() + { + $container = new ContainerBuilder(); + + $container->setParameter('class_name', __NAMESPACE__.'\Foo'); + $container->register('foo', '%class_name%'); + $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar'); + $barDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertEquals('foo', $container->getDefinition('bar')->getArgument(0)); + } + + public function testOptionalParameter() + { + $container = new ContainerBuilder(); + + $container->register('a', __NAMESPACE__.'\A'); + $container->register('foo', __NAMESPACE__.'\Foo'); + $optDefinition = $container->register('opt', __NAMESPACE__.'\OptionalParameter'); + $optDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $definition = $container->getDefinition('opt'); + $this->assertNull($definition->getArgument(0)); + $this->assertEquals('a', $definition->getArgument(1)); + $this->assertEquals('foo', $definition->getArgument(2)); + } + + public function testDontTriggeruAutowiring() + { + $container = new ContainerBuilder(); + + $container->register('foo', __NAMESPACE__.'\Foo'); + $container->register('bar', __NAMESPACE__.'\Bar'); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertCount(0, $container->getDefinition('bar')->getArguments()); + } +} + +class Foo +{ +} + +class Bar +{ + public function __construct(Foo $foo) + { + } +} + +class A +{ +} + +class B extends A +{ +} + +class C +{ + public function __construct(A $a) + { + } +} + +interface DInterface +{ +} + +interface EInterface extends DInterface +{ +} + +class F implements EInterface +{ +} + +class G +{ + public function __construct(DInterface $d, EInterface $e) + { + } +} + +class H +{ + public function __construct(B $b, DInterface $d) + { + } +} + +interface CollisionInterface +{ +} + +class CollisionA implements CollisionInterface +{ +} + +class CollisionB implements CollisionInterface +{ +} + +class CannotBeAutowired +{ + public function __construct(CollisionInterface $collision) + { + } +} + +class Lille +{ +} + +class Dunglas +{ + public function __construct(Lille $l) + { + } +} + +class LesTilleuls +{ + public function __construct(Dunglas $k) + { + } +} + +class OptionalParameter +{ + public function __construct(CollisionInterface $c = null, A $a, Foo $f = null) + { + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 047a7cf334..ad83f8e0d1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -890,6 +890,19 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase $this->assertTrue($classInList); } + + public function testAutowiring() + { + $container = new ContainerBuilder(); + + $container->register('a', __NAMESPACE__.'\A'); + $bDefinition = $container->register('b', __NAMESPACE__.'\B'); + $bDefinition->setAutowired(true); + + $container->compile(); + + $this->assertEquals('a', (string) $container->getDefinition('b')->getArgument(0)); + } } class FooClass @@ -903,3 +916,14 @@ class ProjectContainer extends ContainerBuilder throw new InactiveScopeException('foo', 'request'); } } + +class A +{ +} + +class B +{ + public function __construct(A $a) + { + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php index ea3045df1a..cb6344e495 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php @@ -394,4 +394,25 @@ class DefinitionTest extends \PHPUnit_Framework_TestCase $this->assertSame($def, $def->setProperty('foo', 'bar')); $this->assertEquals(array('foo' => 'bar'), $def->getProperties()); } + + public function testAutowired() + { + $def = new Definition('stdClass'); + $this->assertFalse($def->isAutowired()); + $def->setAutowired(true); + $this->assertTrue($def->isAutowired()); + } + + public function testTypes() + { + $def = new Definition('stdClass'); + + $this->assertEquals(array(), $def->getAutowiringTypes()); + $this->assertSame($def, $def->setAutowiringTypes(array('Foo'))); + $this->assertEquals(array('Foo'), $def->getAutowiringTypes()); + $this->assertSame($def, $def->addAutowiringType('Bar')); + $this->assertTrue($def->hasAutowiringType('Bar')); + $this->assertSame($def, $def->removeAutowiringType('Foo')); + $this->assertEquals(array('Bar'), $def->getAutowiringTypes()); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services22.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services22.xml new file mode 100644 index 0000000000..fa79d38948 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services22.xml @@ -0,0 +1,9 @@ + + + + + Bar + Baz + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services23.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services23.xml new file mode 100644 index 0000000000..3f9e15fd49 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services23.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_types1.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_types1.yml new file mode 100644 index 0000000000..891e01497c --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_types1.yml @@ -0,0 +1,5 @@ +services: + foo_service: + class: FooClass + # types is not an array + autowiring_types: 1 diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_types2.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_types2.yml new file mode 100644 index 0000000000..fb1d53e151 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_types2.yml @@ -0,0 +1,5 @@ +services: + foo_service: + class: FooClass + # autowiring_types is not a string + autowiring_types: [ 1 ] diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services22.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services22.yml new file mode 100644 index 0000000000..55d015baea --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services22.yml @@ -0,0 +1,8 @@ +services: + foo_service: + class: FooClass + autowiring_types: [ Foo, Bar ] + + baz_service: + class: Baz + autowiring_types: Foo diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services23.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services23.yml new file mode 100644 index 0000000000..1984c17714 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services23.yml @@ -0,0 +1,4 @@ +services: + bar_service: + class: BarClass + autowire: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index b717113bb4..723d30605e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -494,4 +494,22 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase $this->assertSame('Baz', $barConfigurator[0]->getClass()); $this->assertSame('configureBar', $barConfigurator[1]); } + + public function testType() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services22.xml'); + + $this->assertEquals(array('Bar', 'Baz'), $container->getDefinition('foo')->getAutowiringTypes()); + } + + public function testAutowire() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services23.xml'); + + $this->assertTrue($container->getDefinition('bar')->isAutowired()); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index 2d1b0b6956..8ab714e43a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -282,4 +282,41 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase $this->assertEquals(array(true), $definition->getArguments()); $this->assertEquals(array('manager' => array(array('alias' => 'user'))), $definition->getTags()); } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + */ + public function testTypesNotArray() + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('bad_types1.yml'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + */ + public function testTypeNotString() + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('bad_types2.yml'); + } + + public function testTypes() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services22.yml'); + + $this->assertEquals(array('Foo', 'Bar'), $container->getDefinition('foo_service')->getAutowiringTypes()); + $this->assertEquals(array('Foo'), $container->getDefinition('baz_service')->getAutowiringTypes()); + } + + public function testAutowire() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services23.yml'); + + $this->assertTrue($container->getDefinition('bar_service')->isAutowired()); + } }