From 6dd53c7209549aced717beec797657b7d6af1d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Wed, 5 Oct 2016 22:46:29 +0200 Subject: [PATCH] [DependencyInjection] Introduce method injection for autowiring --- .../Compiler/AutowirePass.php | 133 ++++++++++---- .../Compiler/LoggingFormatter.php | 5 + .../DependencyInjection/Definition.php | 37 +++- .../Loader/XmlFileLoader.php | 13 ++ .../Loader/YamlFileLoader.php | 6 +- .../schema/dic/services/services-1.0.xsd | 1 + .../Tests/Compiler/AutowirePassTest.php | 164 ++++++++++++++++++ .../Tests/DefinitionTest.php | 10 ++ .../Tests/Fixtures/xml/services27.xml | 9 + .../Tests/Fixtures/xml/services28.xml | 8 + .../Tests/Fixtures/yaml/services27.yml | 4 + .../Tests/Loader/XmlFileLoaderTest.php | 14 ++ .../Tests/Loader/YamlFileLoaderTest.php | 4 + 13 files changed, 375 insertions(+), 33 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services27.xml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services28.xml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services27.yml diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index d1b1fd292c..7aa2d2a64e 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -24,6 +24,9 @@ use Symfony\Component\DependencyInjection\Reference; */ class AutowirePass implements CompilerPassInterface { + /** + * @var ContainerBuilder + */ private $container; private $reflectionClasses = array(); private $definedTypes = array(); @@ -41,8 +44,8 @@ class AutowirePass implements CompilerPassInterface try { $this->container = $container; foreach ($container->getDefinitions() as $id => $definition) { - if ($definition->isAutowired()) { - $this->completeDefinition($id, $definition); + if ($autowiredMethods = $definition->getAutowiredMethods()) { + $this->completeDefinition($id, $definition, $autowiredMethods); } } } finally { @@ -72,8 +75,10 @@ class AutowirePass implements CompilerPassInterface $metadata['__construct'] = self::getResourceMetadataForMethod($constructor); } - foreach (self::getSetters($reflectionClass) as $reflectionMethod) { - $metadata[$reflectionMethod->name] = self::getResourceMetadataForMethod($reflectionMethod); + foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) { + if (!$reflectionMethod->isStatic()) { + $metadata[$reflectionMethod->name] = self::getResourceMetadataForMethod($reflectionMethod); + } } return new AutowireServiceResource($reflectionClass->name, $reflectionClass->getFileName(), $metadata); @@ -84,10 +89,11 @@ class AutowirePass implements CompilerPassInterface * * @param string $id * @param Definition $definition + * @param string[] $autowiredMethods * * @throws RuntimeException */ - private function completeDefinition($id, Definition $definition) + private function completeDefinition($id, Definition $definition, array $autowiredMethods) { if (!$reflectionClass = $this->getReflectionClass($id, $definition)) { return; @@ -97,12 +103,75 @@ class AutowirePass implements CompilerPassInterface $this->container->addResource(static::createResourceForClass($reflectionClass)); } - if (!$constructor = $reflectionClass->getConstructor()) { - return; + $methodsCalled = array(); + foreach ($definition->getMethodCalls() as $methodCall) { + $methodsCalled[$methodCall[0]] = true; } - $arguments = $definition->getArguments(); - foreach ($constructor->getParameters() as $index => $parameter) { + foreach ($this->getMethodsToAutowire($id, $reflectionClass, $autowiredMethods) as $reflectionMethod) { + if (!isset($methodsCalled[$reflectionMethod->name])) { + $this->autowireMethod($id, $definition, $reflectionMethod); + } + } + } + + /** + * Gets the list of methods to autowire. + * + * @param string $id + * @param \ReflectionClass $reflectionClass + * @param string[] $autowiredMethods + * + * @return \ReflectionMethod[] + */ + private function getMethodsToAutowire($id, \ReflectionClass $reflectionClass, array $autowiredMethods) + { + $found = array(); + $regexList = array(); + foreach ($autowiredMethods as $pattern) { + $regexList[] = '/^'.str_replace('\*', '.*', preg_quote($pattern, '/')).'$/i'; + } + + foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) { + if ($reflectionMethod->isStatic()) { + continue; + } + + foreach ($regexList as $k => $regex) { + if (preg_match($regex, $reflectionMethod->name)) { + $found[] = $autowiredMethods[$k]; + yield $reflectionMethod; + + continue 2; + } + } + } + + if ($notFound = array_diff($autowiredMethods, $found)) { + $compiler = $this->container->getCompiler(); + $compiler->addLogMessage($compiler->getLoggingFormatter()->formatUnusedAutowiringPatterns($this, $id, $notFound)); + } + } + + /** + * Autowires the constructor or a setter. + * + * @param string $id + * @param Definition $definition + * @param \ReflectionMethod $reflectionMethod + * + * @throws RuntimeException + */ + private function autowireMethod($id, Definition $definition, \ReflectionMethod $reflectionMethod) + { + if ($isConstructor = $reflectionMethod->isConstructor()) { + $arguments = $definition->getArguments(); + } else { + $arguments = array(); + } + + $addMethodCall = false; // Whether the method should be added to the definition as a call or as arguments + foreach ($reflectionMethod->getParameters() as $index => $parameter) { if (array_key_exists($index, $arguments) && '' !== $arguments[$index]) { continue; } @@ -111,7 +180,11 @@ class AutowirePass implements CompilerPassInterface if (!$typeHint = $parameter->getClass()) { // no default value? Then fail if (!$parameter->isOptional()) { - throw new RuntimeException(sprintf('Unable to autowire argument index %d ($%s) for the service "%s". If this is an object, give it a type-hint. Otherwise, specify this argument\'s value explicitly.', $index, $parameter->name, $id)); + if ($isConstructor) { + throw new RuntimeException(sprintf('Unable to autowire argument index %d ($%s) for the service "%s". If this is an object, give it a type-hint. Otherwise, specify this argument\'s value explicitly.', $index, $parameter->name, $id)); + } + + return; } // specifically pass the default value @@ -126,16 +199,23 @@ class AutowirePass implements CompilerPassInterface if (isset($this->types[$typeHint->name])) { $value = new Reference($this->types[$typeHint->name]); + $addMethodCall = true; } else { try { $value = $this->createAutowiredDefinition($typeHint, $id); + $addMethodCall = true; } catch (RuntimeException $e) { if ($parameter->allowsNull()) { $value = null; } elseif ($parameter->isDefaultValueAvailable()) { $value = $parameter->getDefaultValue(); } else { - throw $e; + // The exception code is set to 1 if the exception must be thrown even if it's a setter + if (1 === $e->getCode() || $isConstructor) { + throw $e; + } + + return; } } } @@ -143,7 +223,11 @@ class AutowirePass implements CompilerPassInterface // Typehint against a non-existing class if (!$parameter->isDefaultValueAvailable()) { - throw new RuntimeException(sprintf('Cannot autowire argument %s for %s because the type-hinted class does not exist (%s).', $index + 1, $definition->getClass(), $e->getMessage()), 0, $e); + if ($isConstructor) { + throw new RuntimeException(sprintf('Cannot autowire argument %s for %s because the type-hinted class does not exist (%s).', $index + 1, $definition->getClass(), $e->getMessage()), 0, $e); + } + + return; } $value = $parameter->getDefaultValue(); @@ -155,7 +239,12 @@ class AutowirePass implements CompilerPassInterface // it's possible index 1 was set, then index 0, then 2, etc // make sure that we re-order so they're injected as expected ksort($arguments); - $definition->setArguments($arguments); + + if ($isConstructor) { + $definition->setArguments($arguments); + } elseif ($addMethodCall) { + $definition->addMethodCall($reflectionMethod->name, $arguments); + } } /** @@ -253,7 +342,7 @@ class AutowirePass implements CompilerPassInterface $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class'; $matchingServices = implode(', ', $this->ambiguousServiceTypes[$typeHint->name]); - throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". Multiple services exist for this %s (%s).', $typeHint->name, $id, $classOrInterface, $matchingServices)); + throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". Multiple services exist for this %s (%s).', $typeHint->name, $id, $classOrInterface, $matchingServices), 1); } if (!$typeHint->isInstantiable()) { @@ -269,7 +358,7 @@ class AutowirePass implements CompilerPassInterface $this->populateAvailableType($argumentId, $argumentDefinition); try { - $this->completeDefinition($argumentId, $argumentDefinition); + $this->completeDefinition($argumentId, $argumentDefinition, array('__construct')); } catch (RuntimeException $e) { $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class'; $message = sprintf('Unable to autowire argument of type "%s" for the service "%s". No services were found matching this %s and it cannot be auto-registered.', $typeHint->name, $id, $classOrInterface); @@ -320,20 +409,6 @@ class AutowirePass implements CompilerPassInterface $this->ambiguousServiceTypes[$type][] = $id; } - /** - * @param \ReflectionClass $reflectionClass - * - * @return \ReflectionMethod[] - */ - private static function getSetters(\ReflectionClass $reflectionClass) - { - foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) { - if (!$reflectionMethod->isStatic() && 1 === $reflectionMethod->getNumberOfParameters() && 0 === strpos($reflectionMethod->name, 'set')) { - yield $reflectionMethod; - } - } - } - private static function getResourceMetadataForMethod(\ReflectionMethod $method) { $methodArgumentsMetadata = array(); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/LoggingFormatter.php b/src/Symfony/Component/DependencyInjection/Compiler/LoggingFormatter.php index db208fa0d6..485b1d300d 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/LoggingFormatter.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/LoggingFormatter.php @@ -38,6 +38,11 @@ class LoggingFormatter return $this->format($pass, sprintf('Resolving inheritance for "%s" (parent: %s).', $childId, $parentId)); } + public function formatUnusedAutowiringPatterns(CompilerPassInterface $pass, $id, array $patterns) + { + return $this->format($pass, sprintf('Autowiring\'s patterns "%s" for service "%s" don\'t match any method.', implode('", "', $patterns), $id)); + } + public function format(CompilerPassInterface $pass, $message) { return sprintf('%s: %s', get_class($pass), $message); diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index 5f40ae41ab..c3d8609c36 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -36,7 +36,7 @@ class Definition private $abstract = false; private $lazy = false; private $decoratedService; - private $autowired = false; + private $autowiredMethods = array(); private $autowiringTypes = array(); protected $arguments; @@ -662,19 +662,50 @@ class Definition */ public function isAutowired() { - return $this->autowired; + return !empty($this->autowiredMethods); + } + + /** + * Gets autowired methods. + * + * @return string[] + */ + public function getAutowiredMethods() + { + return $this->autowiredMethods; } /** * Sets autowired. * + * Allowed values: + * - true: constructor autowiring, same as $this->setAutowiredMethods(array('__construct')) + * - false: no autowiring, same as $this->setAutowiredMethods(array()) + * * @param bool $autowired * * @return Definition The current instance */ public function setAutowired($autowired) { - $this->autowired = $autowired; + $this->autowiredMethods = $autowired ? array('__construct') : array(); + + return $this; + } + + /** + * Sets autowired methods. + * + * Example of allowed value: + * - array('__construct', 'set*', 'initialize'): autowire whitelisted methods only + * + * @param string[] $autowiredMethods + * + * @return Definition The current instance + */ + public function setAutowiredMethods(array $autowiredMethods) + { + $this->autowiredMethods = $autowiredMethods; return $this; } diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 77420ba98d..8ea7d553c6 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -239,6 +239,19 @@ class XmlFileLoader extends FileLoader $definition->addAutowiringType($type->textContent); } + $autowireTags = array(); + foreach ($this->getChildren($service, 'autowire') as $type) { + $autowireTags[] = $type->textContent; + } + + if ($autowireTags) { + if ($service->hasAttribute('autowire')) { + throw new InvalidArgumentException(sprintf('The "autowire" attribute cannot be used together with "" tags for service "%s" in %s.', (string) $service->getAttribute('id'), $file)); + } + + $definition->setAutowiredMethods($autowireTags); + } + 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 06cc506e67..3674a9791c 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -302,7 +302,11 @@ class YamlFileLoader extends FileLoader } if (isset($service['autowire'])) { - $definition->setAutowired($service['autowire']); + if (is_array($service['autowire'])) { + $definition->setAutowiredMethods($service['autowire']); + } else { + $definition->setAutowired($service['autowire']); + } } if (isset($service['autowiring_types'])) { 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 182e09e857..2f0637a997 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 @@ -100,6 +100,7 @@ + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index a8b1be3550..0b8ab9374a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -429,6 +429,74 @@ class AutowirePassTest extends \PHPUnit_Framework_TestCase ); } + public function testSetterInjection() + { + $container = new ContainerBuilder(); + $container->register('app_foo', Foo::class); + $container->register('app_a', A::class); + $container->register('app_collision_a', CollisionA::class); + $container->register('app_collision_b', CollisionB::class); + + // manually configure *one* call, to override autowiring + $container + ->register('setter_injection', SetterInjection::class) + ->setAutowiredMethods(array('__construct', 'set*')) + ->addMethodCall('setWithCallsConfigured', array('manual_arg1', 'manual_arg2')) + ; + + $pass = new AutowirePass(); + $pass->process($container); + + $methodCalls = $container->getDefinition('setter_injection')->getMethodCalls(); + + // grab the call method names + $actualMethodNameCalls = array_map(function ($call) { + return $call[0]; + }, $methodCalls); + $this->assertEquals( + array('setWithCallsConfigured', 'setFoo', 'setDependencies'), + $actualMethodNameCalls + ); + + // test setWithCallsConfigured args + $this->assertEquals( + array('manual_arg1', 'manual_arg2'), + $methodCalls[0][1] + ); + // test setFoo args + $this->assertEquals( + array(new Reference('app_foo')), + $methodCalls[1][1] + ); + } + + public function testExplicitMethodInjection() + { + $container = new ContainerBuilder(); + $container->register('app_foo', Foo::class); + $container->register('app_a', A::class); + $container->register('app_collision_a', CollisionA::class); + $container->register('app_collision_b', CollisionB::class); + + $container + ->register('setter_injection', SetterInjection::class) + ->setAutowiredMethods(array('setFoo', 'notASetter')) + ; + + $pass = new AutowirePass(); + $pass->process($container); + + $methodCalls = $container->getDefinition('setter_injection')->getMethodCalls(); + + $actualMethodNameCalls = array_map(function ($call) { + return $call[0]; + }, $methodCalls); + $this->assertEquals( + array('setFoo', 'notASetter'), + $actualMethodNameCalls + ); + } + /** * @dataProvider getCreateResourceTests */ @@ -476,6 +544,37 @@ class AutowirePassTest extends \PHPUnit_Framework_TestCase $this->assertTrue($container->hasDefinition('bar')); } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "setter_injection_collision". Multiple services exist for this interface (c1, c2). + * @expectedExceptionCode 1 + */ + public function testSetterInjectionCollisionThrowsException() + { + $container = new ContainerBuilder(); + + $container->register('c1', CollisionA::class); + $container->register('c2', CollisionB::class); + $aDefinition = $container->register('setter_injection_collision', SetterInjectionCollision::class); + $aDefinition->setAutowiredMethods(array('__construct', 'set*')); + + $pass = new AutowirePass(); + $pass->process($container); + } + + public function testLogUnusedPatterns() + { + $container = new ContainerBuilder(); + + $definition = $container->register('foo', Foo::class); + $definition->setAutowiredMethods(array('not', 'exist*')); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertEquals(array(AutowirePass::class.': Autowiring\'s patterns "not", "exist*" for service "foo" don\'t match any method.'), $container->getCompiler()->getLog()); + } } class Foo @@ -648,9 +747,74 @@ class ClassForResource class IdenticalClassResource extends ClassForResource { } + class ClassChangedConstructorArgs extends ClassForResource { public function __construct($foo, Bar $bar, $baz) { } } + +class SetterInjection +{ + public function setFoo(Foo $foo) + { + // should be called + } + + public function setDependencies(Foo $foo, A $a) + { + // should be called + } + + public function setBar() + { + // should not be called + } + + public function setNotAutowireable(NotARealClass $n) + { + // should not be called + } + + public function setArgCannotAutowire($foo) + { + // should not be called + } + + public function setOptionalNotAutowireable(NotARealClass $n = null) + { + // should not be called + } + + public function setOptionalNoTypeHint($foo = null) + { + // should not be called + } + + public function setOptionalArgNoAutowireable($other = 'default_val') + { + // should not be called + } + + public function setWithCallsConfigured(A $a) + { + // this method has a calls configured on it + // should not be called + } + + public function notASetter(A $a) + { + // should be called only when explicitly specified + } +} + +class SetterInjectionCollision +{ + public function setMultipleInstancesForOneArg(CollisionInterface $collision) + { + // The CollisionInterface cannot be autowired - there are multiple + + // should throw an exception + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php index 6a85f8044b..d3ef6ba72e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php @@ -290,6 +290,16 @@ class DefinitionTest extends \PHPUnit_Framework_TestCase $this->assertFalse($def->isAutowired()); $def->setAutowired(true); $this->assertTrue($def->isAutowired()); + $this->assertEquals(array('__construct'), $def->getAutowiredMethods()); + + $def->setAutowiredMethods(array('foo')); + $def->setAutowired(false); + $this->assertSame(array(), $def->getAutowiredMethods()); + $this->assertFalse($def->isAutowired()); + + $def->setAutowiredMethods(array('getFoo', 'getBar')); + $this->assertEquals(array('getFoo', 'getBar'), $def->getAutowiredMethods()); + $this->assertTrue($def->isAutowired()); } public function testTypes() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services27.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services27.xml new file mode 100644 index 0000000000..cda0ef23f5 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services27.xml @@ -0,0 +1,9 @@ + + + + + set* + bar + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services28.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services28.xml new file mode 100644 index 0000000000..b422be383b --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services28.xml @@ -0,0 +1,8 @@ + + + + + setFoo + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services27.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services27.yml new file mode 100644 index 0000000000..00496d54fe --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services27.yml @@ -0,0 +1,4 @@ +services: + autowire_array: + class: Foo + autowire: ['set*', 'bar'] diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index 5a166d963d..257461e294 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -554,6 +554,20 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase $loader->load('services23.xml'); $this->assertTrue($container->getDefinition('bar')->isAutowired()); + $this->assertEquals(array('__construct'), $container->getDefinition('bar')->getAutowiredMethods()); + + $loader->load('services27.xml'); + $this->assertEquals(array('set*', 'bar'), $container->getDefinition('autowire_array')->getAutowiredMethods()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + */ + public function testAutowireAttributeAndTag() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services28.xml'); } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index fabaa4859c..e26374dd34 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -324,6 +324,10 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase $loader->load('services23.yml'); $this->assertTrue($container->getDefinition('bar_service')->isAutowired()); + $this->assertEquals(array('__construct'), $container->getDefinition('bar_service')->getAutowiredMethods()); + + $loader->load('services27.yml'); + $this->assertEquals(array('set*', 'bar'), $container->getDefinition('autowire_array')->getAutowiredMethods()); } /**