diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index 35449b35677d..0ec55596f044 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -16,7 +16,6 @@ use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper; -use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\TypedReference; /** @@ -76,9 +75,9 @@ public static function createResourceForClass(\ReflectionClass $reflectionClass) */ protected function processValue($value, $isRoot = false) { - if ($value instanceof TypedReference && !$this->container->has((string) $value)) { - if ($ref = $this->getAutowiredReference($value->getType(), $value->canBeAutoregistered())) { - $value = new TypedReference((string) $ref, $value->getType(), $value->getInvalidBehavior(), $value->canBeAutoregistered()); + if ($value instanceof TypedReference) { + if ($ref = $this->getAutowiredReference($value)) { + $value = $ref; } else { $this->container->log($this, $this->createTypeNotFoundMessage($value->getType(), 'typed reference')); } @@ -242,7 +241,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a continue; } - if (!$value = $this->getAutowiredReference($type, !$parameter->isOptional())) { + if (!$value = $this->getAutowiredReference(new TypedReference($type, $type, !$parameter->isOptional() ? $class : ''))) { $failureMessage = $this->createTypeNotFoundMessage($type, sprintf('argument "$%s" of method "%s()"', $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method)); if ($parameter->isDefaultValueAvailable()) { @@ -276,16 +275,14 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a } /** - * @return Reference|null A reference to the service matching the given type, if any + * @return TypedReference|null A reference to the service matching the given type, if any */ - private function getAutowiredReference($type, $autoRegister) + private function getAutowiredReference(TypedReference $reference) { - if ($this->container->has($type) && !$this->container->findDefinition($type)->isAbstract()) { - return new Reference($type); - } + $type = $reference->getType(); - if (isset($this->autowired[$type])) { - return $this->autowired[$type] ? new Reference($this->autowired[$type]) : null; + if ($type !== (string) $reference || ($this->container->has($type) && !$this->container->findDefinition($type)->isAbstract())) { + return $reference; } if (null === $this->types) { @@ -293,12 +290,18 @@ private function getAutowiredReference($type, $autoRegister) } if (isset($this->definedTypes[$type])) { - return new Reference($this->types[$type]); + return new TypedReference($this->types[$type], $type); } - if ($autoRegister && !isset($this->types[$type]) && !isset($this->ambiguousServiceTypes[$type])) { - return $this->createAutowiredDefinition($type); + if (!$reference->canBeAutoregistered() || isset($this->types[$type]) || isset($this->ambiguousServiceTypes[$type])) { + return; } + + if (isset($this->autowired[$type])) { + return $this->autowired[$type] ? new TypedReference($this->autowired[$type], $type) : null; + } + + return $this->createAutowiredDefinition($type); } /** @@ -384,7 +387,7 @@ private function set($type, $id) * * @param string $type * - * @return Reference|null A reference to the registered definition + * @return TypedReference|null A reference to the registered definition */ private function createAutowiredDefinition($type) { @@ -412,7 +415,7 @@ private function createAutowiredDefinition($type) $this->container->log($this, sprintf('Type "%s" has been auto-registered for service "%s".', $type, $this->currentId)); - return new Reference($argumentId); + return new TypedReference($argumentId, $type); } private function createTypeNotFoundMessage($type, $label) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php b/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php index a02265afe1e1..861393006cf8 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php @@ -71,10 +71,11 @@ protected function processValue($value, $isRoot = false) } $this->container->addObjectResource($class); $subscriberMap = array(); + $declaringClass = (new \ReflectionMethod($class, 'getSubscribedServices'))->class; foreach ($class::getSubscribedServices() as $key => $type) { if (!is_string($type) || !preg_match('/^\??[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+$/', $type)) { - throw new InvalidArgumentException(sprintf('%s::getSubscribedServices() must return valid PHP types for service "%s" key "%s", "%s" returned.', $class, $this->currentId, $key, is_string($type) ? $type : gettype($type))); + throw new InvalidArgumentException(sprintf('"%s::getSubscribedServices()" must return valid PHP types for service "%s" key "%s", "%s" returned.', $class, $this->currentId, $key, is_string($type) ? $type : gettype($type))); } if ($optionalBehavior = '?' === $type[0]) { $type = substr($type, 1); @@ -85,18 +86,18 @@ protected function processValue($value, $isRoot = false) } if (!isset($serviceMap[$key])) { if (!$autowire) { - throw new InvalidArgumentException(sprintf('Service "%s" misses a "container.service_subscriber" tag with "key"/"id" attributes corresponding to entry "%s" as returned by %s::getSubscribedServices().', $this->currentId, $key, $class)); + throw new InvalidArgumentException(sprintf('Service "%s" misses a "container.service_subscriber" tag with "key"/"id" attributes corresponding to entry "%s" as returned by "%s::getSubscribedServices()".', $this->currentId, $key, $class)); } $serviceMap[$key] = new Reference($type); } - $subscriberMap[$key] = new TypedReference((string) $serviceMap[$key], $type, $optionalBehavior ?: ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE); + $subscriberMap[$key] = new TypedReference((string) $serviceMap[$key], $type, $declaringClass, $optionalBehavior ?: ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE); unset($serviceMap[$key]); } if ($serviceMap = array_keys($serviceMap)) { $message = sprintf(1 < count($serviceMap) ? 'keys "%s" do' : 'key "%s" does', str_replace('%', '%%', implode('", "', $serviceMap))); - throw new InvalidArgumentException(sprintf('Service %s not exist in the map returned by %s::getSubscribedServices() for service "%s".', $message, $class, $this->currentId)); + throw new InvalidArgumentException(sprintf('Service %s not exist in the map returned by "%s::getSubscribedServices()" for service "%s".', $message, $class, $this->currentId)); } $serviceLocator = $this->serviceLocator; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index f962e58f500c..f0ec4d66410b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -368,9 +368,9 @@ public function testSomeSpecificArgumentsAreSet() $definition = $container->getDefinition('multiple'); $this->assertEquals( array( - new Reference(A::class), + new TypedReference(A::class, A::class, MultipleArguments::class), new Reference('foo'), - new Reference(Dunglas::class), + new TypedReference(Dunglas::class, Dunglas::class, MultipleArguments::class), ), $definition->getArguments() ); @@ -423,10 +423,10 @@ public function testOptionalScalarArgsDontMessUpOrder() $definition = $container->getDefinition('with_optional_scalar'); $this->assertEquals( array( - new Reference(A::class), + new TypedReference(A::class, A::class, MultipleArgumentsOptionalScalar::class), // use the default value 'default_val', - new Reference(Lille::class), + new TypedReference(Lille::class, Lille::class), ), $definition->getArguments() ); @@ -447,8 +447,8 @@ public function testOptionalScalarArgsNotPassedIfLast() $definition = $container->getDefinition('with_optional_scalar_last'); $this->assertEquals( array( - new Reference(A::class), - new Reference(Lille::class), + new TypedReference(A::class, A::class, MultipleArgumentsOptionalScalarLast::class), + new TypedReference(Lille::class, Lille::class, MultipleArgumentsOptionalScalarLast::class), ), $definition->getArguments() ); @@ -486,7 +486,7 @@ public function testSetterInjection() ); // test setFoo args $this->assertEquals( - array(new Reference(Foo::class)), + array(new TypedReference(Foo::class, Foo::class, SetterInjection::class)), $methodCalls[1][1] ); } @@ -515,7 +515,7 @@ public function testExplicitMethodInjection() array_column($methodCalls, 0) ); $this->assertEquals( - array(new Reference(A::class)), + array(new TypedReference(A::class, A::class, SetterInjection::class)), $methodCalls[0][1] ); } @@ -526,7 +526,7 @@ public function testTypedReference() $container ->register('bar', Bar::class) - ->setProperty('a', array(new TypedReference(A::class, A::class))) + ->setProperty('a', array(new TypedReference(A::class, A::class, Bar::class))) ; $pass = new AutowirePass(); @@ -629,7 +629,7 @@ public function testEmptyStringIsKept() (new ResolveClassPass())->process($container); (new AutowirePass())->process($container); - $this->assertEquals(array(new Reference(A::class), '', new Reference(Lille::class)), $container->getDefinition('foo')->getArguments()); + $this->assertEquals(array(new TypedReference(A::class, A::class, MultipleArgumentsOptionalScalar::class), '', new TypedReference(Lille::class, Lille::class)), $container->getDefinition('foo')->getArguments()); } public function testWithFactory() @@ -644,7 +644,7 @@ public function testWithFactory() (new ResolveClassPass())->process($container); (new AutowirePass())->process($container); - $this->assertEquals(array(new Reference(Foo::class)), $definition->getArguments()); + $this->assertEquals(array(new TypedReference(Foo::class, Foo::class, A::class)), $definition->getArguments()); } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php index 75c10bbdc788..d399eb3a6de5 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php @@ -18,6 +18,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ServiceLocator; +use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition; +use Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber; use Symfony\Component\DependencyInjection\TypedReference; require_once __DIR__.'/../Fixtures/includes/classes.php'; @@ -32,7 +34,7 @@ public function testInvalidClass() { $container = new ContainerBuilder(); - $container->register('foo', 'stdClass') + $container->register('foo', CustomDefinition::class) ->addTag('container.service_subscriber') ; @@ -48,7 +50,7 @@ public function testInvalidAttributes() { $container = new ContainerBuilder(); - $container->register('foo', 'TestServiceSubscriber') + $container->register('foo', TestServiceSubscriber::class) ->addTag('container.service_subscriber', array('bar' => '123')) ; @@ -60,7 +62,7 @@ public function testNoAttributes() { $container = new ContainerBuilder(); - $container->register('foo', 'TestServiceSubscriber') + $container->register('foo', TestServiceSubscriber::class) ->addArgument(new Reference('container')) ->addTag('container.service_subscriber') ; @@ -75,10 +77,10 @@ public function testNoAttributes() $this->assertSame(ServiceLocator::class, $locator->getClass()); $expected = array( - 'TestServiceSubscriber' => new ServiceClosureArgument(new TypedReference('TestServiceSubscriber', 'TestServiceSubscriber')), - 'stdClass' => new ServiceClosureArgument(new TypedReference('stdClass', 'stdClass', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)), - 'bar' => new ServiceClosureArgument(new TypedReference('stdClass', 'stdClass')), - 'baz' => new ServiceClosureArgument(new TypedReference('stdClass', 'stdClass', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)), + TestServiceSubscriber::class => new ServiceClosureArgument(new TypedReference(TestServiceSubscriber::class, TestServiceSubscriber::class, TestServiceSubscriber::class)), + CustomDefinition::class => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, TestServiceSubscriber::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)), + 'bar' => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, TestServiceSubscriber::class)), + 'baz' => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, TestServiceSubscriber::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)), ); $this->assertEquals($expected, $locator->getArgument(0)); @@ -88,7 +90,7 @@ public function testWithAttributes() { $container = new ContainerBuilder(); - $container->register('foo', 'TestServiceSubscriber') + $container->register('foo', TestServiceSubscriber::class) ->setAutowired(true) ->addArgument(new Reference('container')) ->addTag('container.service_subscriber', array('key' => 'bar', 'id' => 'bar')) @@ -105,10 +107,10 @@ public function testWithAttributes() $this->assertSame(ServiceLocator::class, $locator->getClass()); $expected = array( - 'TestServiceSubscriber' => new ServiceClosureArgument(new TypedReference('TestServiceSubscriber', 'TestServiceSubscriber')), - 'stdClass' => new ServiceClosureArgument(new TypedReference('stdClass', 'stdClass', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)), - 'bar' => new ServiceClosureArgument(new TypedReference('bar', 'stdClass')), - 'baz' => new ServiceClosureArgument(new TypedReference('stdClass', 'stdClass', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)), + TestServiceSubscriber::class => new ServiceClosureArgument(new TypedReference(TestServiceSubscriber::class, TestServiceSubscriber::class, TestServiceSubscriber::class)), + CustomDefinition::class => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, TestServiceSubscriber::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)), + 'bar' => new ServiceClosureArgument(new TypedReference('bar', CustomDefinition::class, TestServiceSubscriber::class)), + 'baz' => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, TestServiceSubscriber::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)), ); $this->assertEquals($expected, $locator->getArgument(0)); @@ -116,20 +118,20 @@ public function testWithAttributes() /** * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - * @expectedExceptionMessage Service key "test" does not exist in the map returned by TestServiceSubscriber::getSubscribedServices() for service "foo_service". + * @expectedExceptionMessage Service key "test" does not exist in the map returned by "Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber::getSubscribedServices()" for service "foo_service". */ public function testExtraServiceSubscriber() { $container = new ContainerBuilder(); - $container->register('foo_service', 'TestServiceSubscriber') + $container->register('foo_service', TestServiceSubscriber::class) ->setAutowired(true) ->addArgument(new Reference('container')) ->addTag('container.service_subscriber', array( 'key' => 'test', - 'id' => 'TestServiceSubscriber', + 'id' => TestServiceSubscriber::class, )) ; - $container->register('TestServiceSubscriber', 'TestServiceSubscriber'); + $container->register(TestServiceSubscriber::class, TestServiceSubscriber::class); $container->compile(); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 4c38325e1be4..1ef7b8771b8d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -25,6 +25,7 @@ use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; use Symfony\Component\DependencyInjection\ServiceLocator; +use Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber; use Symfony\Component\DependencyInjection\Variable; use Symfony\Component\ExpressionLanguage\Expression; @@ -557,15 +558,15 @@ public function testServiceLocator() public function testServiceSubscriber() { $container = new ContainerBuilder(); - $container->register('foo_service', 'TestServiceSubscriber') + $container->register('foo_service', TestServiceSubscriber::class) ->setAutowired(true) ->addArgument(new Reference('container')) ->addTag('container.service_subscriber', array( 'key' => 'bar', - 'id' => 'TestServiceSubscriber', + 'id' => TestServiceSubscriber::class, )) ; - $container->register('TestServiceSubscriber', 'TestServiceSubscriber'); + $container->register(TestServiceSubscriber::class, TestServiceSubscriber::class); $container->compile(); $dumper = new PhpDumper($container); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriber.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriber.php new file mode 100644 index 000000000000..875abe9e02e1 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriber.php @@ -0,0 +1,22 @@ + CustomDefinition::class, + 'baz' => '?'.CustomDefinition::class, + ); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php index c4c93c123e2a..8ccbbcd438ca 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php @@ -108,20 +108,3 @@ public function __construct($lazyValues) $this->lazyValues = $lazyValues; } } - -class TestServiceSubscriber implements ServiceSubscriberInterface -{ - public function __construct($container) - { - } - - public static function getSubscribedServices() - { - return array( - __CLASS__, - '?stdClass', - 'bar' => 'stdClass', - 'baz' => '?stdClass', - ); - } -} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php index 018934cd3d96..19611a1e4be7 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php @@ -28,19 +28,19 @@ public function __construct() { $this->services = array(); $this->normalizedIds = array( - 'autowired.stdclass' => 'autowired.stdClass', + 'autowired.symfony\\component\\dependencyinjection\\tests\\fixtures\\customdefinition' => 'autowired.Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition', 'psr\\container\\containerinterface' => 'Psr\\Container\\ContainerInterface', - 'stdclass' => 'stdClass', 'symfony\\component\\dependencyinjection\\containerinterface' => 'Symfony\\Component\\DependencyInjection\\ContainerInterface', - 'testservicesubscriber' => 'TestServiceSubscriber', + 'symfony\\component\\dependencyinjection\\tests\\fixtures\\customdefinition' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition', + 'symfony\\component\\dependencyinjection\\tests\\fixtures\\testservicesubscriber' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber', ); $this->methodMap = array( - 'TestServiceSubscriber' => 'getTestServiceSubscriberService', - 'autowired.stdClass' => 'getAutowired_StdClassService', + 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => 'getSymfony_Component_DependencyInjection_Tests_Fixtures_TestServiceSubscriberService', + 'autowired.Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => 'getAutowired_Symfony_Component_DependencyInjection_Tests_Fixtures_CustomDefinitionService', 'foo_service' => 'getFooServiceService', ); $this->privates = array( - 'autowired.stdClass' => true, + 'autowired.Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => true, ); $this->aliases = array(); @@ -73,16 +73,16 @@ public function isFrozen() } /** - * Gets the 'TestServiceSubscriber' service. + * Gets the 'Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber' service. * * This service is shared. * This method always returns the same instance of the service. * - * @return \TestServiceSubscriber A TestServiceSubscriber instance + * @return \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber A Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber instance */ - protected function getTestServiceSubscriberService() + protected function getSymfony_Component_DependencyInjection_Tests_Fixtures_TestServiceSubscriberService() { - return $this->services['TestServiceSubscriber'] = new \TestServiceSubscriber(); + return $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber(); } /** @@ -93,23 +93,23 @@ protected function getTestServiceSubscriberService() * * This service is autowired. * - * @return \TestServiceSubscriber A TestServiceSubscriber instance + * @return \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber A Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber instance */ protected function getFooServiceService() { - return $this->services['foo_service'] = new \TestServiceSubscriber(new \Symfony\Component\DependencyInjection\ServiceLocator(array('TestServiceSubscriber' => function () { - $f = function (\TestServiceSubscriber $v) { return $v; }; return $f(${($_ = isset($this->services['TestServiceSubscriber']) ? $this->services['TestServiceSubscriber'] : $this->get('TestServiceSubscriber')) && false ?: '_'}); + return $this->services['foo_service'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber(new \Symfony\Component\DependencyInjection\ServiceLocator(array('Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => function () { + $f = function (\Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition $v) { return $v; }; return $f(${($_ = isset($this->services['autowired.Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition']) ? $this->services['autowired.Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] : $this->getAutowired_Symfony_Component_DependencyInjection_Tests_Fixtures_CustomDefinitionService()) && false ?: '_'}); + }, 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => function () { + $f = function (\Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber $v) { return $v; }; return $f(${($_ = isset($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber']) ? $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] : $this->get('Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber')) && false ?: '_'}); }, 'bar' => function () { - $f = function (\stdClass $v) { return $v; }; return $f(${($_ = isset($this->services['TestServiceSubscriber']) ? $this->services['TestServiceSubscriber'] : $this->get('TestServiceSubscriber')) && false ?: '_'}); + $f = function (\Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition $v) { return $v; }; return $f(${($_ = isset($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber']) ? $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] : $this->get('Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber')) && false ?: '_'}); }, 'baz' => function () { - $f = function (\stdClass $v = null) { return $v; }; return $f(${($_ = isset($this->services['autowired.stdClass']) ? $this->services['autowired.stdClass'] : $this->getAutowired_StdClassService()) && false ?: '_'}); - }, 'stdClass' => function () { - $f = function (\stdClass $v = null) { return $v; }; return $f(${($_ = isset($this->services['autowired.stdClass']) ? $this->services['autowired.stdClass'] : $this->getAutowired_StdClassService()) && false ?: '_'}); + $f = function (\Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition $v) { return $v; }; return $f(${($_ = isset($this->services['autowired.Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition']) ? $this->services['autowired.Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] : $this->getAutowired_Symfony_Component_DependencyInjection_Tests_Fixtures_CustomDefinitionService()) && false ?: '_'}); }))); } /** - * Gets the 'autowired.stdClass' service. + * Gets the 'autowired.Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition' service. * * This service is shared. * This method always returns the same instance of the service. @@ -120,10 +120,10 @@ protected function getFooServiceService() * * This service is autowired. * - * @return \stdClass A stdClass instance + * @return \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition A Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition instance */ - protected function getAutowired_StdClassService() + protected function getAutowired_Symfony_Component_DependencyInjection_Tests_Fixtures_CustomDefinitionService() { - return $this->services['autowired.stdClass'] = new \stdClass(); + return $this->services['autowired.Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition(); } } diff --git a/src/Symfony/Component/DependencyInjection/TypedReference.php b/src/Symfony/Component/DependencyInjection/TypedReference.php index bb57bedb5252..aad78e806b6b 100644 --- a/src/Symfony/Component/DependencyInjection/TypedReference.php +++ b/src/Symfony/Component/DependencyInjection/TypedReference.php @@ -19,19 +19,19 @@ class TypedReference extends Reference { private $type; - private $canBeAutoregistered; + private $requiringClass; /** - * @param string $id The service identifier - * @param string $type The PHP type of the identified service - * @param int $invalidBehavior The behavior when the service does not exist - * @param bool $canBeAutoregistered Whether autowiring can autoregister the referenced service when it's a FQCN or not + * @param string $id The service identifier + * @param string $type The PHP type of the identified service + * @param string $requiringClass The class of the service that requires the referenced type + * @param int $invalidBehavior The behavior when the service does not exist */ - public function __construct($id, $type, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $canBeAutoregistered = true) + public function __construct($id, $type, $requiringClass = '', $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { parent::__construct($id, $invalidBehavior); $this->type = $type; - $this->canBeAutoregistered = $canBeAutoregistered; + $this->requiringClass = $requiringClass; } public function getType() @@ -39,8 +39,13 @@ public function getType() return $this->type; } + public function getRequiringClass() + { + return $this->requiringClass; + } + public function canBeAutoregistered() { - return $this->canBeAutoregistered; + return $this->requiringClass && (false !== $i = strpos($this->type, '\\')) && 0 === strncasecmp($this->type, $this->requiringClass, 1 + $i); } } diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php index ee1202b20a19..2b563a74d724 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php @@ -52,6 +52,7 @@ public function process(ContainerBuilder $container) continue; } $class = $def->getClass(); + $autowire = $def->isAutowired(); // resolve service class, taking parent definitions into account while (!$class && $def instanceof ChildDefinition) { @@ -76,6 +77,7 @@ public function process(ContainerBuilder $container) // validate and collect explicit per-actions and per-arguments service references foreach ($tags as $attributes) { if (!isset($attributes['action']) && !isset($attributes['argument']) && !isset($attributes['id'])) { + $autowire = true; continue; } foreach (array('action', 'argument', 'id') as $k) { @@ -120,11 +122,11 @@ public function process(ContainerBuilder $container) } elseif ($p->allowsNull() && !$p->isOptional()) { $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; } - } elseif (!$type) { + } elseif (!$type || !$autowire) { continue; } - $args[$p->name] = $type ? new TypedReference($target, $type, $invalidBehavior, false) : new Reference($target, $invalidBehavior); + $args[$p->name] = $type ? new TypedReference($target, $type, $r->class, $invalidBehavior) : new Reference($target, $invalidBehavior); } // register the maps as a per-method service-locators if ($args) { diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php index 50ad4c8be0dc..e3d62f8f4000 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php @@ -43,7 +43,7 @@ public function process(ContainerBuilder $container) if (!$argumentLocator->getArgument(0)) { // remove empty argument locators - $reason = sprintf('Removing service-argument-resolver for controller "%s": no corresponding definitions were found for the referenced services/types.%s', $controller, !$argumentLocator->isAutowired() ? ' Did you forget to enable autowiring?' : ''); + $reason = sprintf('Removing service-argument resolver for controller "%s": no corresponding services exist for the referenced types.', $controller); } else { // any methods listed for call-at-instantiation cannot be actions $reason = false; diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php index b03f34d972f1..bfae7efde304 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php @@ -145,7 +145,7 @@ public function testAllActions() $this->assertSame(ServiceLocator::class, $locator->getClass()); $this->assertFalse($locator->isPublic()); - $expected = array('bar' => new ServiceClosureArgument(new TypedReference('stdClass', 'stdClass', ContainerInterface::IGNORE_ON_INVALID_REFERENCE, false))); + $expected = array('bar' => new ServiceClosureArgument(new TypedReference(ControllerDummy::class, ControllerDummy::class, RegisterTestController::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE))); $this->assertEquals($expected, $locator->getArgument(0)); } @@ -165,7 +165,7 @@ public function testExplicitArgument() $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0); $locator = $container->getDefinition((string) $locator['foo:fooAction']->getValues()[0]); - $expected = array('bar' => new ServiceClosureArgument(new TypedReference('bar', 'stdClass', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false))); + $expected = array('bar' => new ServiceClosureArgument(new TypedReference('bar', ControllerDummy::class, RegisterTestController::class))); $this->assertEquals($expected, $locator->getArgument(0)); } @@ -184,22 +184,26 @@ public function testOptionalArgument() $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0); $locator = $container->getDefinition((string) $locator['foo:fooAction']->getValues()[0]); - $expected = array('bar' => new ServiceClosureArgument(new TypedReference('bar', 'stdClass', ContainerInterface::IGNORE_ON_INVALID_REFERENCE, false))); + $expected = array('bar' => new ServiceClosureArgument(new TypedReference('bar', ControllerDummy::class, RegisterTestController::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE))); $this->assertEquals($expected, $locator->getArgument(0)); } } class RegisterTestController { - public function __construct(\stdClass $bar) + public function __construct(ControllerDummy $bar) { } - public function fooAction(\stdClass $bar) + public function fooAction(ControllerDummy $bar) { } - protected function barAction(\stdClass $bar) + protected function barAction(ControllerDummy $bar) { } } + +class ControllerDummy +{ +} diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPassTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPassTest.php index 786c6f7ed834..06764b11b9ee 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPassTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPassTest.php @@ -53,7 +53,7 @@ public function testProcess() $this->assertSame(array('bar'), array_keys($container->getDefinition((string) $controllers['c1:fooAction']->getValues()[0])->getArgument(0))); $expectedLog = array( - 'Symfony\Component\HttpKernel\DependencyInjection\RemoveEmptyControllerArgumentLocatorsPass: Removing service-argument-resolver for controller "c2:fooAction": no corresponding definitions were found for the referenced services/types. Did you forget to enable autowiring?', + 'Symfony\Component\HttpKernel\DependencyInjection\RemoveEmptyControllerArgumentLocatorsPass: Removing service-argument resolver for controller "c2:fooAction": no corresponding services exist for the referenced types.', 'Symfony\Component\HttpKernel\DependencyInjection\RemoveEmptyControllerArgumentLocatorsPass: Removing method "setTestCase" of service "c2" from controller candidates: the method is called at instantiation, thus cannot be an action.', );