[DI] Restrict autowired registration to "same-vendor" namespaces

This commit is contained in:
Nicolas Grekas 2017-04-06 10:48:39 +02:00
parent ab93feae3f
commit 9c53b3deb0
13 changed files with 130 additions and 107 deletions

View File

@ -16,7 +16,6 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
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 @@ class AutowirePass extends AbstractRecursivePass
*/
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 @@ class AutowirePass extends AbstractRecursivePass
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 @@ class AutowirePass extends AbstractRecursivePass
}
/**
* @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 @@ class AutowirePass extends AbstractRecursivePass
}
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 @@ class AutowirePass extends AbstractRecursivePass
*
* @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 @@ class AutowirePass extends AbstractRecursivePass
$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)

View File

@ -71,10 +71,11 @@ class RegisterServiceSubscribersPass extends AbstractRecursivePass
}
$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 @@ class RegisterServiceSubscribersPass extends AbstractRecursivePass
}
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;

View File

@ -368,9 +368,9 @@ class AutowirePassTest extends TestCase
$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 @@ class AutowirePassTest extends TestCase
$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 @@ class AutowirePassTest extends TestCase
$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 @@ class AutowirePassTest extends TestCase
);
// 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 @@ class AutowirePassTest extends TestCase
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 @@ class AutowirePassTest extends TestCase
$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 @@ class AutowirePassTest extends TestCase
(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 @@ class AutowirePassTest extends TestCase
(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());
}
/**

View File

@ -18,6 +18,8 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
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 @@ class RegisterServiceSubscribersPassTest extends TestCase
{
$container = new ContainerBuilder();
$container->register('foo', 'stdClass')
$container->register('foo', CustomDefinition::class)
->addTag('container.service_subscriber')
;
@ -48,7 +50,7 @@ class RegisterServiceSubscribersPassTest extends TestCase
{
$container = new ContainerBuilder();
$container->register('foo', 'TestServiceSubscriber')
$container->register('foo', TestServiceSubscriber::class)
->addTag('container.service_subscriber', array('bar' => '123'))
;
@ -60,7 +62,7 @@ class RegisterServiceSubscribersPassTest extends TestCase
{
$container = new ContainerBuilder();
$container->register('foo', 'TestServiceSubscriber')
$container->register('foo', TestServiceSubscriber::class)
->addArgument(new Reference('container'))
->addTag('container.service_subscriber')
;
@ -75,10 +77,10 @@ class RegisterServiceSubscribersPassTest extends TestCase
$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 @@ class RegisterServiceSubscribersPassTest extends TestCase
{
$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 @@ class RegisterServiceSubscribersPassTest extends TestCase
$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 @@ class RegisterServiceSubscribersPassTest extends TestCase
/**
* @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();
}
}

View File

@ -25,6 +25,7 @@ use Symfony\Component\DependencyInjection\TypedReference;
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 @@ class PhpDumperTest extends TestCase
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);

View File

@ -0,0 +1,22 @@
<?php
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
class TestServiceSubscriber implements ServiceSubscriberInterface
{
public function __construct($container)
{
}
public static function getSubscribedServices()
{
return array(
__CLASS__,
'?'.CustomDefinition::class,
'bar' => CustomDefinition::class,
'baz' => '?'.CustomDefinition::class,
);
}
}

View File

@ -108,20 +108,3 @@ class LazyContext
$this->lazyValues = $lazyValues;
}
}
class TestServiceSubscriber implements ServiceSubscriberInterface
{
public function __construct($container)
{
}
public static function getSubscribedServices()
{
return array(
__CLASS__,
'?stdClass',
'bar' => 'stdClass',
'baz' => '?stdClass',
);
}
}

View File

@ -28,19 +28,19 @@ class ProjectServiceContainer extends Container
{
$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 @@ class ProjectServiceContainer extends Container
}
/**
* 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 @@ class ProjectServiceContainer extends Container
*
* 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 @@ class ProjectServiceContainer extends Container
*
* 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();
}
}

View File

@ -19,19 +19,19 @@ namespace Symfony\Component\DependencyInjection;
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 @@ class TypedReference extends Reference
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);
}
}

View File

@ -52,6 +52,7 @@ class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface
continue;
}
$class = $def->getClass();
$autowire = $def->isAutowired();
// resolve service class, taking parent definitions into account
while (!$class && $def instanceof ChildDefinition) {
@ -76,6 +77,7 @@ class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface
// 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 @@ class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface
} 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) {

View File

@ -43,7 +43,7 @@ class RemoveEmptyControllerArgumentLocatorsPass implements CompilerPassInterface
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;

View File

@ -145,7 +145,7 @@ class RegisterControllerArgumentLocatorsPassTest extends TestCase
$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 @@ class RegisterControllerArgumentLocatorsPassTest extends TestCase
$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 @@ class RegisterControllerArgumentLocatorsPassTest extends TestCase
$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
{
}

View File

@ -53,7 +53,7 @@ class RemoveEmptyControllerArgumentLocatorsPassTest extends TestCase
$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.',
);