diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php index bbe869b935e5c..ddc7d004da40d 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php @@ -90,7 +90,7 @@ protected function getConstructor(Definition $definition, $required) { if (is_string($factory = $definition->getFactory())) { if (!function_exists($factory)) { - throw new RuntimeException(sprintf('Unable to resolve service "%s": function "%s" does not exist.', $this->currentId, $factory)); + throw new RuntimeException(sprintf('Invalid service "%s": function "%s" does not exist.', $this->currentId, $factory)); } $r = new \ReflectionFunction($factory); if (false !== $r->getFileName() && file_exists($r->getFileName())) { @@ -108,7 +108,7 @@ protected function getConstructor(Definition $definition, $required) $class = $definition->getClass(); } if ('__construct' === $method) { - throw new RuntimeException(sprintf('Unable to resolve service "%s": "__construct()" cannot be used as a factory method.', $this->currentId)); + throw new RuntimeException(sprintf('Invalid service "%s": "__construct()" cannot be used as a factory method.', $this->currentId)); } return $this->getReflectionMethod(new Definition($class), $method); @@ -117,14 +117,14 @@ protected function getConstructor(Definition $definition, $required) $class = $definition->getClass(); if (!$r = $this->container->getReflectionClass($class)) { - throw new RuntimeException(sprintf('Unable to resolve service "%s": class "%s" does not exist.', $this->currentId, $class)); + throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class)); } if (!$r = $r->getConstructor()) { if ($required) { - throw new RuntimeException(sprintf('Unable to resolve service "%s": class%s has no constructor.', $this->currentId, sprintf($class !== $this->currentId ? ' "%s"' : '', $class))); + throw new RuntimeException(sprintf('Invalid service "%s": class%s has no constructor.', $this->currentId, sprintf($class !== $this->currentId ? ' "%s"' : '', $class))); } } elseif (!$r->isPublic()) { - throw new RuntimeException(sprintf('Unable to resolve service "%s": %s must be public.', $this->currentId, sprintf($class !== $this->currentId ? 'constructor of class "%s"' : 'its constructor', $class))); + throw new RuntimeException(sprintf('Invalid service "%s": %s must be public.', $this->currentId, sprintf($class !== $this->currentId ? 'constructor of class "%s"' : 'its constructor', $class))); } return $r; @@ -145,20 +145,20 @@ protected function getReflectionMethod(Definition $definition, $method) } if (!$class = $definition->getClass()) { - throw new RuntimeException(sprintf('Unable to resolve service "%s": the class is not set.', $this->currentId)); + throw new RuntimeException(sprintf('Invalid service "%s": the class is not set.', $this->currentId)); } if (!$r = $this->container->getReflectionClass($class)) { - throw new RuntimeException(sprintf('Unable to resolve service "%s": class "%s" does not exist.', $this->currentId, $class)); + throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class)); } if (!$r->hasMethod($method)) { - throw new RuntimeException(sprintf('Unable to resolve service "%s": method "%s()" does not exist.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method)); + throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" does not exist.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method)); } $r = $r->getMethod($method); if (!$r->isPublic()) { - throw new RuntimeException(sprintf('Unable to resolve service "%s": method "%s()" must be public.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method)); + throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" must be public.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method)); } return $r; diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index 8cb1978ef675d..ace116fb9b515 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -130,7 +130,6 @@ private function doProcessValue($value, $isRoot = false) return $value; } - $autowiredMethods = $this->getMethodsToAutowire($reflectionClass); $methodCalls = $value->getMethodCalls(); try { @@ -143,7 +142,7 @@ private function doProcessValue($value, $isRoot = false) array_unshift($methodCalls, array($constructor, $value->getArguments())); } - $methodCalls = $this->autowireCalls($reflectionClass, $methodCalls, $autowiredMethods); + $methodCalls = $this->autowireCalls($reflectionClass, $methodCalls); if ($constructor) { list(, $arguments) = array_shift($methodCalls); @@ -161,61 +160,18 @@ private function doProcessValue($value, $isRoot = false) } /** - * Gets the list of methods to autowire. - * * @param \ReflectionClass $reflectionClass - * - * @return \ReflectionMethod[] - */ - private function getMethodsToAutowire(\ReflectionClass $reflectionClass) - { - $methodsToAutowire = array(); - - foreach ($reflectionClass->getMethods() as $reflectionMethod) { - $r = $reflectionMethod; - - if ($r->isConstructor()) { - continue; - } - - while (true) { - if (false !== $doc = $r->getDocComment()) { - if (false !== stripos($doc, '@required') && preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) { - $methodsToAutowire[strtolower($reflectionMethod->name)] = $reflectionMethod; - break; - } - if (false === stripos($doc, '@inheritdoc') || !preg_match('#(?:^/\*\*|\n\s*+\*)\s*+(?:\{@inheritdoc\}|@inheritdoc)(?:\s|\*/$)#i', $doc)) { - break; - } - } - try { - $r = $r->getPrototype(); - } catch (\ReflectionException $e) { - break; // method has no prototype - } - } - } - - return $methodsToAutowire; - } - - /** - * @param \ReflectionClass $reflectionClass - * @param array $methodCalls - * @param \ReflectionMethod[] $autowiredMethods + * @param array $methodCalls * * @return array */ - private function autowireCalls(\ReflectionClass $reflectionClass, array $methodCalls, array $autowiredMethods) + private function autowireCalls(\ReflectionClass $reflectionClass, array $methodCalls) { foreach ($methodCalls as $i => $call) { list($method, $arguments) = $call; if ($method instanceof \ReflectionFunctionAbstract) { $reflectionMethod = $method; - } elseif (isset($autowiredMethods[$lcMethod = strtolower($method)]) && $autowiredMethods[$lcMethod]->isPublic()) { - $reflectionMethod = $autowiredMethods[$lcMethod]; - unset($autowiredMethods[$lcMethod]); } else { $reflectionMethod = $this->getReflectionMethod(new Definition($reflectionClass->name), $method); } @@ -227,16 +183,6 @@ private function autowireCalls(\ReflectionClass $reflectionClass, array $methodC } } - foreach ($autowiredMethods as $lcMethod => $reflectionMethod) { - $method = $reflectionMethod->name; - - if (!$reflectionMethod->isPublic()) { - $class = $reflectionClass->name; - throw new AutowiringFailedException($this->currentId, sprintf('Cannot autowire service "%s": method "%s()" must be public.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method)); - } - $methodCalls[] = array($method, $this->autowireMethod($reflectionMethod, array())); - } - return $methodCalls; } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php new file mode 100644 index 0000000000000..6c744f88f6a94 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php @@ -0,0 +1,70 @@ + + * + * 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\Definition; + +/** + * Looks for definitions with autowiring enabled and registers their corresponding "@required" methods as setters. + * + * @author Nicolas Grekas + */ +class AutowireRequiredMethodsPass extends AbstractRecursivePass +{ + /** + * {@inheritdoc} + */ + protected function processValue($value, $isRoot = false) + { + $value = parent::processValue($value, $isRoot); + + if (!$value instanceof Definition || !$value->isAutowired() || $value->isAbstract() || !$value->getClass()) { + return $value; + } + if (!$reflectionClass = $this->container->getReflectionClass($value->getClass(), false)) { + return $value; + } + + $alreadyCalledMethods = array(); + + foreach ($value->getMethodCalls() as list($method)) { + $alreadyCalledMethods[strtolower($method)] = true; + } + + foreach ($reflectionClass->getMethods() as $reflectionMethod) { + $r = $reflectionMethod; + + if ($r->isConstructor() || isset($alreadyCalledMethods[strtolower($r->name)])) { + continue; + } + + while (true) { + if (false !== $doc = $r->getDocComment()) { + if (false !== stripos($doc, '@required') && preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) { + $value->addMethodCall($reflectionMethod->name); + break; + } + if (false === stripos($doc, '@inheritdoc') || !preg_match('#(?:^/\*\*|\n\s*+\*)\s*+(?:\{@inheritdoc\}|@inheritdoc)(?:\s|\*/$)#i', $doc)) { + break; + } + } + try { + $r = $r->getPrototype(); + } catch (\ReflectionException $e) { + break; // method has no prototype + } + } + } + + return $value; + } +} diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php index 79792bee4a106..e80b8e6cc8623 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -58,6 +58,7 @@ public function __construct() new CheckDefinitionValidityPass(), new RegisterServiceSubscribersPass(), new ResolveNamedArgumentsPass(), + new AutowireRequiredMethodsPass(), new ResolveBindingsPass(), $autowirePass = new AutowirePass(false), new ResolveServiceSubscribersPass(), diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php index e4d592d0c6473..a96f981d074b4 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php @@ -61,11 +61,11 @@ protected function processValue($value, $isRoot = false) } } - throw new InvalidArgumentException(sprintf('Unable to resolve service "%s": method "%s()" has no argument named "%s". Check your service definition.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method, $key)); + throw new InvalidArgumentException(sprintf('Invalid service "%s": method "%s()" has no argument named "%s". Check your service definition.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method, $key)); } if (null !== $argument && !$argument instanceof Reference && !$argument instanceof Definition) { - throw new InvalidArgumentException(sprintf('Unable to resolve service "%s": the value of argument "%s" of method "%s()" must be null, an instance of %s or an instance of %s, %s given.', $this->currentId, $key, $class !== $this->currentId ? $class.'::'.$method : $method, Reference::class, Definition::class, gettype($argument))); + throw new InvalidArgumentException(sprintf('Invalid service "%s": the value of argument "%s" of method "%s()" must be null, an instance of %s or an instance of %s, %s given.', $this->currentId, $key, $class !== $this->currentId ? $class.'::'.$method : $method, Reference::class, Definition::class, gettype($argument))); } foreach ($parameters as $j => $p) { @@ -76,7 +76,7 @@ protected function processValue($value, $isRoot = false) } } - throw new InvalidArgumentException(sprintf('Unable to resolve service "%s": method "%s()" has no argument type-hinted as "%s". Check your service definition.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method, $key)); + throw new InvalidArgumentException(sprintf('Invalid service "%s": method "%s()" has no argument type-hinted as "%s". Check your service definition.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method, $key)); } if ($resolvedArguments !== $call[1]) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index 94666df608391..dbf839b4eac83 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredMethodsPass; use Symfony\Component\DependencyInjection\Compiler\AutowirePass; use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -20,6 +21,8 @@ use Symfony\Component\DependencyInjection\Tests\Fixtures\includes\FooVariadic; use Symfony\Component\DependencyInjection\TypedReference; +require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php'; + /** * @author Kévin Dunglas */ @@ -174,7 +177,7 @@ public function testExceptionsAreStored() /** * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException - * @expectedExceptionMessage Unable to resolve service "private_service": constructor of class "Symfony\Component\DependencyInjection\Tests\Compiler\PrivateConstructor" must be public. + * @expectedExceptionMessage Invalid service "private_service": constructor of class "Symfony\Component\DependencyInjection\Tests\Compiler\PrivateConstructor" must be public. */ public function testPrivateConstructorThrowsAutowireException() { @@ -554,6 +557,7 @@ public function testSetterInjection() ; (new ResolveClassPass())->process($container); + (new AutowireRequiredMethodsPass())->process($container); (new AutowirePass())->process($container); $methodCalls = $container->getDefinition('setter_injection')->getMethodCalls(); @@ -590,6 +594,7 @@ public function testExplicitMethodInjection() ; (new ResolveClassPass())->process($container); + (new AutowireRequiredMethodsPass())->process($container); (new AutowirePass())->process($container); $methodCalls = $container->getDefinition('setter_injection')->getMethodCalls(); @@ -687,6 +692,8 @@ public function testSetterInjectionCollisionThrowsException() $aDefinition = $container->register('setter_injection_collision', SetterInjectionCollision::class); $aDefinition->setAutowired(true); + (new AutowireRequiredMethodsPass())->process($container); + $pass = new AutowirePass(); $pass->process($container); } @@ -770,6 +777,7 @@ public function testNotWireableCalls($method, $expectedMsg) } (new ResolveClassPass())->process($container); + (new AutowireRequiredMethodsPass())->process($container); (new AutowirePass())->process($container); } @@ -778,7 +786,7 @@ public function provideNotWireableCalls() return array( array('setNotAutowireable', 'Cannot autowire service "foo": argument "$n" of method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setNotAutowireable()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\NotARealClass" but this class cannot be loaded.'), array('setDifferentNamespace', 'Cannot autowire service "foo": argument "$n" of method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setDifferentNamespace()" references class "stdClass" but no such service exists. It cannot be auto-registered because it is from a different root namespace.'), - array(null, 'Cannot autowire service "foo": method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setProtectedMethod()" must be public.'), + array(null, 'Invalid service "foo": method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setProtectedMethod()" must be public.'), ); } @@ -859,333 +867,3 @@ public function testExceptionWhenAliasDoesNotExist() $pass->process($container); } } - -class Foo -{ -} - -class Bar -{ - public function __construct(Foo $foo) - { - } -} - -interface AInterface -{ -} - -class A implements AInterface -{ - public static function create(Foo $foo) - { - } -} - -class B extends A -{ -} - -class C -{ - public function __construct(A $a) - { - } -} - -interface DInterface -{ -} - -interface EInterface extends DInterface -{ -} - -interface IInterface -{ -} - -class I implements IInterface -{ -} - -class F extends I implements EInterface -{ -} - -class G -{ - public function __construct(DInterface $d, EInterface $e, IInterface $i) - { - } -} - -class H -{ - public function __construct(B $b, DInterface $d) - { - } -} - -class D -{ - public function __construct(A $a, DInterface $d) - { - } -} - -class E -{ - public function __construct(D $d = null) - { - } -} - -class J -{ - public function __construct(I $i) - { - } -} - -interface CollisionInterface -{ -} - -class CollisionA implements CollisionInterface -{ -} - -class CollisionB implements CollisionInterface -{ -} - -class CannotBeAutowired -{ - public function __construct(CollisionInterface $collision) - { - } -} - -class CannotBeAutowiredForwardOrder -{ - public function __construct(CollisionA $a, CollisionInterface $b, CollisionB $c) - { - } -} - -class CannotBeAutowiredReverseOrder -{ - public function __construct(CollisionA $a, CollisionB $c, CollisionInterface $b) - { - } -} - -class Lille -{ -} - -class Dunglas -{ - public function __construct(Lille $l) - { - } -} - -class LesTilleuls -{ - public function __construct(Dunglas $j, Dunglas $k) - { - } -} - -class OptionalParameter -{ - public function __construct(CollisionInterface $c = null, A $a, Foo $f = null) - { - } -} - -class BadTypeHintedArgument -{ - public function __construct(Dunglas $k, NotARealClass $r) - { - } -} -class BadParentTypeHintedArgument -{ - public function __construct(Dunglas $k, OptionalServiceClass $r) - { - } -} -class NotGuessableArgument -{ - public function __construct(Foo $k) - { - } -} -class NotGuessableArgumentForSubclass -{ - public function __construct(A $k) - { - } -} -class MultipleArguments -{ - public function __construct(A $k, $foo, Dunglas $dunglas) - { - } -} - -class MultipleArgumentsOptionalScalar -{ - public function __construct(A $a, $foo = 'default_val', Lille $lille = null) - { - } -} -class MultipleArgumentsOptionalScalarLast -{ - public function __construct(A $a, Lille $lille, $foo = 'some_val') - { - } -} -class MultipleArgumentsOptionalScalarNotReallyOptional -{ - public function __construct(A $a, $foo = 'default_val', Lille $lille) - { - } -} - -/* - * Classes used for testing createResourceForClass - */ -class ClassForResource -{ - public function __construct($foo, Bar $bar = null) - { - } - - public function setBar(Bar $bar) - { - } -} -class IdenticalClassResource extends ClassForResource -{ -} - -class ClassChangedConstructorArgs extends ClassForResource -{ - public function __construct($foo, Bar $bar, $baz) - { - } -} - -class SetterInjection extends SetterInjectionParent -{ - /** - * @required - */ - public function setFoo(Foo $foo) - { - // should be called - } - - /** @inheritdoc*/ - public function setDependencies(Foo $foo, A $a) - { - // should be called - } - - /** {@inheritdoc} */ - public function setWithCallsConfigured(A $a) - { - // this method has a calls configured on it - } - - public function notASetter(A $a) - { - // should be called only when explicitly specified - } - - /** - * @required*/ - public function setChildMethodWithoutDocBlock(A $a) - { - } -} - -class SetterInjectionParent -{ - /** @required*/ - public function setDependencies(Foo $foo, A $a) - { - // should be called - } - - public function notASetter(A $a) - { - // @required should be ignored when the child does not add @inheritdoc - } - - /** @required prefix is on purpose */ - public function setWithCallsConfigured(A $a) - { - } - - /** @required */ - public function setChildMethodWithoutDocBlock(A $a) - { - } -} - -class SetterInjectionCollision -{ - /** - * @required - */ - public function setMultipleInstancesForOneArg(CollisionInterface $collision) - { - // The CollisionInterface cannot be autowired - there are multiple - - // should throw an exception - } -} - -class NotWireable -{ - public function setNotAutowireable(NotARealClass $n) - { - } - - public function setBar() - { - } - - public function setOptionalNotAutowireable(NotARealClass $n = null) - { - } - - public function setDifferentNamespace(\stdClass $n) - { - } - - public function setOptionalNoTypeHint($foo = null) - { - } - - public function setOptionalArgNoAutowireable($other = 'default_val') - { - } - - /** @required */ - protected function setProtectedMethod(A $a) - { - } -} - -class PrivateConstructor -{ - private function __construct() - { - } -} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredMethodsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredMethodsPassTest.php new file mode 100644 index 0000000000000..3b067150ff5a1 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredMethodsPassTest.php @@ -0,0 +1,80 @@ + + * + * 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 PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass; +use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredMethodsPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php'; + +class AutowireRequiredMethodsPassTest extends TestCase +{ + public function testSetterInjection() + { + $container = new ContainerBuilder(); + $container->register(Foo::class); + $container->register(A::class); + $container->register(CollisionA::class); + $container->register(CollisionB::class); + + // manually configure *one* call, to override autowiring + $container + ->register('setter_injection', SetterInjection::class) + ->setAutowired(true) + ->addMethodCall('setWithCallsConfigured', array('manual_arg1', 'manual_arg2')); + + (new ResolveClassPass())->process($container); + (new AutowireRequiredMethodsPass())->process($container); + + $methodCalls = $container->getDefinition('setter_injection')->getMethodCalls(); + + $this->assertEquals( + array('setWithCallsConfigured', 'setFoo', 'setDependencies', 'setChildMethodWithoutDocBlock'), + array_column($methodCalls, 0) + ); + + // test setWithCallsConfigured args + $this->assertEquals( + array('manual_arg1', 'manual_arg2'), + $methodCalls[0][1] + ); + // test setFoo args + $this->assertEquals(array(), $methodCalls[1][1]); + } + + public function testExplicitMethodInjection() + { + $container = new ContainerBuilder(); + $container->register(Foo::class); + $container->register(A::class); + $container->register(CollisionA::class); + $container->register(CollisionB::class); + + $container + ->register('setter_injection', SetterInjection::class) + ->setAutowired(true) + ->addMethodCall('notASetter', array()); + + (new ResolveClassPass())->process($container); + (new AutowireRequiredMethodsPass())->process($container); + + $methodCalls = $container->getDefinition('setter_injection')->getMethodCalls(); + + $this->assertEquals( + array('notASetter', 'setFoo', 'setDependencies', 'setWithCallsConfigured', 'setChildMethodWithoutDocBlock'), + array_column($methodCalls, 0) + ); + $this->assertEquals(array(), $methodCalls[0][1]); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php index f7e29d2110ff1..16e486afafe2e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Argument\BoundArgument; +use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredMethodsPass; use Symfony\Component\DependencyInjection\Compiler\ResolveBindingsPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; @@ -20,6 +21,8 @@ use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass; use Symfony\Component\DependencyInjection\TypedReference; +require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php'; + class ResolveBindingsPassTest extends TestCase { public function testProcess() @@ -79,4 +82,17 @@ public function testTypedReferenceSupport() $this->assertEquals(array($typedRef), $container->getDefinition('def1')->getArguments()); $this->assertEquals(array(new Reference('foo')), $container->getDefinition('def2')->getArguments()); } + + public function testScalarSetter() + { + $container = new ContainerBuilder(); + + $definition = $container->autowire('foo', ScalarSetter::class); + $definition->setBindings(array('$defaultLocale' => 'fr')); + + (new AutowireRequiredMethodsPass())->process($container); + (new ResolveBindingsPass())->process($container); + + $this->assertEquals(array(array('setDefaultLocale', array('fr'))), $definition->getMethodCalls()); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php new file mode 100644 index 0000000000000..bf99eff6a2838 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php @@ -0,0 +1,343 @@ + prefix is on purpose */ + public function setWithCallsConfigured(A $a) + { + } + + /** @required */ + public function setChildMethodWithoutDocBlock(A $a) + { + } +} + +class NotWireable +{ + public function setNotAutowireable(NotARealClass $n) + { + } + + public function setBar() + { + } + + public function setOptionalNotAutowireable(NotARealClass $n = null) + { + } + + public function setDifferentNamespace(\stdClass $n) + { + } + + public function setOptionalNoTypeHint($foo = null) + { + } + + public function setOptionalArgNoAutowireable($other = 'default_val') + { + } + + /** @required */ + protected function setProtectedMethod(A $a) + { + } +} + +class PrivateConstructor +{ + private function __construct() + { + } +} + +class ScalarSetter +{ + /** + * @required + */ + public function setDefaultLocale($defaultLocale) + { + } +}