[DI] add ServiceLocatorTagPass::register() to share service locators

This commit is contained in:
Nicolas Grekas 2017-03-27 13:10:12 +02:00
parent e3d99649aa
commit 8ff764be82
21 changed files with 176 additions and 158 deletions

View File

@ -11,11 +11,10 @@
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
class TranslatorPass implements CompilerPassInterface
{
@ -46,7 +45,7 @@ class TranslatorPass implements CompilerPassInterface
$container
->findDefinition('translator.default')
->replaceArgument(0, (new Definition(ServiceLocator::class, array($loaderRefs)))->addTag('container.service_locator'))
->replaceArgument(0, ServiceLocatorTagPass::register($container, $loaderRefs))
->replaceArgument(3, $loaders)
;
}

View File

@ -41,11 +41,12 @@ class AddConstraintValidatorsPassTest extends TestCase
$addConstraintValidatorsPass = new AddConstraintValidatorsPass();
$addConstraintValidatorsPass->process($container);
$this->assertEquals((new Definition(ServiceLocator::class, array(array(
$expected = (new Definition(ServiceLocator::class, array(array(
Validator1::class => new ServiceClosureArgument(new Reference('my_constraint_validator_service1')),
'my_constraint_validator_alias1' => new ServiceClosureArgument(new Reference('my_constraint_validator_service1')),
Validator2::class => new ServiceClosureArgument(new Reference('my_constraint_validator_service2')),
))))->addTag('container.service_locator'), $validatorFactory->getArgument(0));
))))->addTag('container.service_locator')->setPublic(false);
$this->assertEquals($expected, $container->getDefinition((string) $validatorFactory->getArgument(0)));
}
public function testThatCompilerPassIsIgnoredIfThereIsNoConstraintValidatorFactoryDefinition()

View File

@ -53,7 +53,7 @@ class FormPassTest extends TestCase
(new Definition(ServiceLocator::class, array(array(
__CLASS__.'_Type1' => new ServiceClosureArgument(new Reference('my.type1')),
__CLASS__.'_Type2' => new ServiceClosureArgument(new Reference('my.type2')),
))))->addTag('container.service_locator'),
))))->addTag('container.service_locator')->setPublic(false),
$extDefinition->getArgument(0)
);
}

View File

@ -12,45 +12,39 @@
namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslatorPass;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslatorPass;
use Symfony\Component\DependencyInjection\ServiceLocator;
class TranslatorPassTest extends TestCase
{
public function testValidCollector()
{
$definition = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->getMock();
$definition->expects($this->at(0))
->method('addMethodCall')
->with('addLoader', array('xliff', new Reference('xliff')));
$definition->expects($this->at(1))
->method('addMethodCall')
->with('addLoader', array('xlf', new Reference('xliff')));
$loader = (new Definition())
->addTag('translation.loader', array('alias' => 'xliff', 'legacy-alias' => 'xlf'));
$translator = (new Definition())
->setArguments(array(null, null, null, null));
$container = new ContainerBuilder();
$container->setDefinition('translator.default', $translator);
$container->setDefinition('translation.loader', $loader);
$container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('hasDefinition', 'getDefinition', 'findTaggedServiceIds', 'findDefinition'))->getMock();
$container->expects($this->any())
->method('hasDefinition')
->will($this->returnValue(true));
$container->expects($this->once())
->method('getDefinition')
->will($this->returnValue($definition));
$container->expects($this->once())
->method('findTaggedServiceIds')
->will($this->returnValue(array('xliff' => array(array('alias' => 'xliff', 'legacy-alias' => 'xlf')))));
$container->expects($this->once())
->method('findDefinition')
->will($this->returnValue($translator = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->getMock()));
$translator->expects($this->at(0))
->method('replaceArgument')
->with(0, $this->equalTo((new Definition(ServiceLocator::class, array(array('xliff' => new Reference('xliff')))))->addTag('container.service_locator')))
->willReturn($translator);
$translator->expects($this->at(1))
->method('replaceArgument')
->with(3, array('xliff' => array('xliff', 'xlf')))
->willReturn($translator);
$pass = new TranslatorPass();
$pass->process($container);
$expected = (new Definition())
->addTag('translation.loader', array('alias' => 'xliff', 'legacy-alias' => 'xlf'))
->addMethodCall('addLoader', array('xliff', new Reference('translation.loader')))
->addMethodCall('addLoader', array('xlf', new Reference('translation.loader')))
;
$this->assertEquals($expected, $loader);
$this->assertSame(array('translation.loader' => array('xliff', 'xlf')), $translator->getArgument(3));
$expected = array('translation.loader' => new ServiceClosureArgument(new Reference('translation.loader')));
$this->assertEquals($expected, $container->getDefinition((string) $translator->getArgument(0))->getArgument(0));
}
}

View File

@ -17,14 +17,13 @@ use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\Console\Application;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Security\Core\Authorization\ExpressionLanguage;
@ -262,10 +261,10 @@ class SecurityExtension extends Extension
->replaceArgument(2, new Reference($configId))
;
$contextRefs[$contextId] = new ServiceClosureArgument(new Reference($contextId));
$contextRefs[$contextId] = new Reference($contextId);
$map[$contextId] = $matcher;
}
$mapDef->replaceArgument(0, (new Definition(ServiceLocator::class, array($contextRefs)))->addTag('container.service_locator'));
$mapDef->replaceArgument(0, ServiceLocatorTagPass::register($container, $contextRefs));
$mapDef->replaceArgument(1, new IteratorArgument($map));
// add authentication providers to authentication manager

View File

@ -11,12 +11,10 @@
namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ServiceLocator;
/**
* Registers Twig runtime services.
@ -38,9 +36,9 @@ class RuntimeLoaderPass implements CompilerPassInterface
continue;
}
$mapping[$def->getClass()] = new ServiceClosureArgument(new Reference($id));
$mapping[$def->getClass()] = new Reference($id);
}
$definition->replaceArgument(0, (new Definition(ServiceLocator::class, array($mapping)))->addTag('container.service_locator'));
$definition->replaceArgument(0, ServiceLocatorTagPass::register($container, $mapping));
}
}

View File

@ -244,7 +244,7 @@ class TwigExtensionTest extends TestCase
$container->compile();
$loader = $container->getDefinition('twig.runtime_loader');
$args = $loader->getArgument(0)->getArgument(0);
$args = $container->getDefinition((string) $loader->getArgument(0))->getArgument(0);
$this->assertArrayHasKey('Symfony\Bridge\Twig\Form\TwigRenderer', $args);
$this->assertArrayHasKey('FooClass', $args);
$this->assertEquals('twig.form.renderer', $args['Symfony\Bridge\Twig\Form\TwigRenderer']->getValues()[0]);

View File

@ -11,8 +11,8 @@
namespace Symfony\Component\DependencyInjection\Argument;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
/**
* Represents a service wrapped in a memoizing closure.
@ -28,11 +28,17 @@ class ServiceClosureArgument implements ArgumentInterface
$this->values = array($reference);
}
/**
* {@inheritdoc}
*/
public function getValues()
{
return $this->values;
}
/**
* {@inheritdoc}
*/
public function setValues(array $values)
{
if (array(0) !== array_keys($values) || !($values[0] instanceof Reference || null === $values[0])) {

View File

@ -11,13 +11,11 @@
namespace Symfony\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\TypedReference;
/**
@ -87,7 +85,7 @@ class RegisterServiceSubscribersPass extends AbstractRecursivePass
$serviceMap[$key] = new Reference($type);
}
$subscriberMap[$key] = new ServiceClosureArgument(new TypedReference((string) $serviceMap[$key], $type, $optionalBehavior ?: ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE));
$subscriberMap[$key] = new TypedReference((string) $serviceMap[$key], $type, $optionalBehavior ?: ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE);
unset($serviceMap[$key]);
}
@ -97,12 +95,7 @@ class RegisterServiceSubscribersPass extends AbstractRecursivePass
}
$serviceLocator = $this->serviceLocator;
$this->serviceLocator = 'container.'.$this->currentId.'.'.md5(serialize($value));
$this->container->register($this->serviceLocator, ServiceLocator::class)
->addArgument($subscriberMap)
->setPublic(false)
->setAutowired($value->isAutowired())
->addTag('container.service_locator');
$this->serviceLocator = (string) ServiceLocatorTagPass::register($this->container, $subscriberMap, $value->getAutowired());
try {
return parent::processValue($value);

View File

@ -11,7 +11,9 @@
namespace Symfony\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
@ -22,7 +24,7 @@ use Symfony\Component\DependencyInjection\ServiceLocator;
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class ServiceLocatorTagPass extends AbstractRecursivePass
final class ServiceLocatorTagPass extends AbstractRecursivePass
{
protected function processValue($value, $isRoot = false)
{
@ -48,7 +50,52 @@ class ServiceLocatorTagPass extends AbstractRecursivePass
}
$arguments[0][$k] = new ServiceClosureArgument($v);
}
ksort($arguments[0]);
return $value->setArguments($arguments);
$value->setArguments($arguments);
if ($public = $value->isPublic()) {
$value->setPublic(false);
}
$id = 'service_locator.'.md5(serialize($value));
if ($isRoot) {
if ($id !== $this->currentId) {
$this->container->setAlias($id, new Alias($this->currentId, $public));
}
return $value;
}
$this->container->setDefinition($id, $value);
return new Reference($id);
}
/**
* @param ContainerBuilder $container
* @param Reference[] $refMap
* @param int|bool $autowired
*
* @return Reference
*/
public static function register(ContainerBuilder $container, array $refMap, $autowired = false)
{
foreach ($refMap as $id => $ref) {
$refMap[$id] = new ServiceClosureArgument($ref);
}
ksort($refMap);
$locator = (new Definition(ServiceLocator::class))
->addArgument($refMap)
->setPublic(false)
->setAutowired($autowired)
->addTag('container.service_locator');
if (!$container->has($id = 'service_locator.'.md5(serialize($locator)))) {
$container->setDefinition($id, $locator);
}
return new Reference($id);
}
}

View File

@ -99,12 +99,12 @@ class ProjectServiceContainer extends Container
{
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 ?: '_'});
}, 'stdClass' => function () {
$f = function (\stdClass $v = null) { return $v; }; return $f(${($_ = isset($this->services['autowired.stdClass']) ? $this->services['autowired.stdClass'] : $this->getAutowired_StdClassService()) && false ?: '_'});
}, 'bar' => function () {
$f = function (\stdClass $v) { return $v; }; return $f(${($_ = isset($this->services['TestServiceSubscriber']) ? $this->services['TestServiceSubscriber'] : $this->get('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 ?: '_'});
})));
}

View File

@ -12,14 +12,13 @@
namespace Symfony\Component\Form\DependencyInjection;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceLocator;
/**
* Adds all services with the tags "form.type", "form.type_extension" and
@ -60,17 +59,16 @@ class FormPass implements CompilerPassInterface
{
// Get service locator argument
$servicesMap = array();
$locator = $definition->getArgument(0);
// Builds an array with fully-qualified type class names as keys and service IDs as values
foreach ($container->findTaggedServiceIds($this->formTypeTag) as $serviceId => $tag) {
$serviceDefinition = $container->getDefinition($serviceId);
// Add form type service to the service locator
$servicesMap[$serviceDefinition->getClass()] = new ServiceClosureArgument(new Reference($serviceId));
$servicesMap[$serviceDefinition->getClass()] = new Reference($serviceId);
}
return (new Definition(ServiceLocator::class, array($servicesMap)))->addTag('container.service_locator');
return ServiceLocatorTagPass::register($container, $servicesMap);
}
private function processFormTypeExtensions(ContainerBuilder $container)

View File

@ -51,7 +51,7 @@ class FormPassTest extends TestCase
(new Definition(ServiceLocator::class, array(array(
__CLASS__.'_Type1' => new ServiceClosureArgument(new Reference('my.type1')),
__CLASS__.'_Type2' => new ServiceClosureArgument(new Reference('my.type2')),
))))->addTag('container.service_locator'),
))))->addTag('container.service_locator')->setPublic(false),
$extDefinition->getArgument(0)
);
}

View File

@ -11,13 +11,11 @@
namespace Symfony\Component\HttpKernel\DependencyInjection;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface;
/**
@ -64,10 +62,10 @@ class FragmentRendererPass implements CompilerPassInterface
}
foreach ($tags as $tag) {
$renderers[$tag['alias']] = new ServiceClosureArgument(new Reference($id));
$renderers[$tag['alias']] = new Reference($id);
}
}
$definition->replaceArgument(0, (new Definition(ServiceLocator::class, array($renderers)))->addTag('container.service_locator'));
$definition->replaceArgument(0, ServiceLocatorTagPass::register($container, $renderers));
}
}

View File

@ -11,15 +11,13 @@
namespace Symfony\Component\HttpKernel\DependencyInjection;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\TypedReference;
/**
@ -54,7 +52,7 @@ class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface
continue;
}
$class = $def->getClass();
$isAutowired = $def->isAutowired();
$autowired = $def->getAutowired();
// resolve service class, taking parent definitions into account
while (!$class && $def instanceof ChildDefinition) {
@ -127,25 +125,16 @@ class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface
continue;
}
$args[$p->name] = new ServiceClosureArgument($type ? new TypedReference($target, $type, $invalidBehavior, false) : new Reference($target, $invalidBehavior));
$args[$p->name] = $type ? new TypedReference($target, $type, $invalidBehavior, false) : new Reference($target, $invalidBehavior);
}
// register the maps as a per-method service-locators
if ($args) {
$argsId = sprintf('arguments.%s:%s', $id, $r->name);
$container->register($argsId, ServiceLocator::class)
->addArgument($args)
->setPublic(false)
->setAutowired($isAutowired)
->addTag('controller.arguments_locator', array($class, $id, $r->name));
$controllers[$id.':'.$r->name] = new ServiceClosureArgument(new Reference($argsId));
if ($id === $class) {
$controllers[$id.'::'.$r->name] = new ServiceClosureArgument(new Reference($argsId));
}
$controllers[$id.':'.$r->name] = ServiceLocatorTagPass::register($container, $args, $autowired);
}
}
}
$container->getDefinition($this->resolverServiceId)
->replaceArgument(0, (new Definition(ServiceLocator::class, array($controllers)))->addTag('container.service_locator'));
->replaceArgument(0, ServiceLocatorTagPass::register($container, $controllers));
}
}

View File

@ -35,37 +35,39 @@ class RemoveEmptyControllerArgumentLocatorsPass implements CompilerPassInterface
}
$serviceResolver = $container->getDefinition($this->resolverServiceId);
$controllers = $serviceResolver->getArgument(0)->getArgument(0);
$controllerLocator = $container->getDefinition((string) $serviceResolver->getArgument(0));
$controllers = $controllerLocator->getArgument(0);
foreach ($container->findTaggedServiceIds('controller.arguments_locator') as $id => $tags) {
$argumentLocator = $container->getDefinition($id)->clearTag('controller.arguments_locator');
list($class, $service, $action) = $tags[0];
foreach ($controllers as $controller => $argumentRef) {
$argumentLocator = $container->getDefinition((string) $argumentRef->getValues()[0]);
if (!$argumentLocator->getArgument(0)) {
// remove empty argument locators
$reason = sprintf('Removing service-argument-resolver for controller "%s:%s": no corresponding definitions were found for the referenced services/types.%s', $service, $action, !$argumentLocator->isAutowired() ? ' Did you forget to enable autowiring?' : '');
$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?' : '');
} else {
// any methods listed for call-at-instantiation cannot be actions
$reason = false;
foreach ($container->getDefinition($service)->getMethodCalls() as list($method, $args)) {
$action = substr(strrchr($controller, ':'), 1);
$id = substr($controller, 0, -1 - strlen($action));
$controllerDef = $container->getDefinition($id);
foreach ($controllerDef->getMethodCalls() as list($method, $args)) {
if (0 === strcasecmp($action, $method)) {
$reason = sprintf('Removing method "%s" of service "%s" from controller candidates: the method is called at instantiation, thus cannot be an action.', $action, $service);
$reason = sprintf('Removing method "%s" of service "%s" from controller candidates: the method is called at instantiation, thus cannot be an action.', $action, $id);
break;
}
}
if (!$reason) {
if ($controllerDef->getClass() === $id) {
$controllers[$id.'::'.$action] = $argumentRef;
}
continue;
}
}
$container->removeDefinition($id);
unset($controllers[$service.':'.$action]);
if ($service === $class) {
unset($controllers[$service.'::'.$action]);
}
unset($controllers[$controller]);
$container->log($this, $reason);
}
$serviceResolver->getArgument(0)->replaceArgument(0, $controllers);
$controllerLocator->replaceArgument(0, $controllers);
}
}

View File

@ -12,10 +12,7 @@
namespace Symfony\Component\HttpKernel\Tests\DependencyInjection;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\DependencyInjection\FragmentRendererPass;
use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface;
@ -65,7 +62,7 @@ class FragmentRendererPassTest extends TestCase
$renderer
->expects($this->once())
->method('replaceArgument')
->with(0, $this->equalTo((new Definition(ServiceLocator::class, array(array('foo' => new ServiceClosureArgument(new Reference('my_content_renderer'))))))->addTag('container.service_locator')));
->with(0, $this->equalTo(new Reference('service_locator.5ae0a401097c64ca63ed976c71bc9642')));
$definition = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->getMock();
$definition->expects($this->atLeastOnce())

View File

@ -15,7 +15,6 @@ use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\TypedReference;
@ -128,7 +127,7 @@ class RegisterControllerArgumentLocatorsPassTest extends TestCase
public function testAllActions()
{
$container = new ContainerBuilder();
$container->register('argument_resolver.service')->addArgument(array());
$resolver = $container->register('argument_resolver.service')->addArgument(array());
$container->register('foo', RegisterTestController::class)
->setAutowired(true)
@ -138,12 +137,12 @@ class RegisterControllerArgumentLocatorsPassTest extends TestCase
$pass = new RegisterControllerArgumentLocatorsPass();
$pass->process($container);
$expected = new Definition(ServiceLocator::class);
$expected->addArgument(array('foo:fooAction' => new ServiceClosureArgument(new Reference('arguments.foo:fooAction'))));
$expected->addTag('container.service_locator');
$this->assertEquals($expected, $container->getDefinition('argument_resolver.service')->getArgument(0));
$locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
$this->assertEquals(array('foo:fooAction' => new ServiceClosureArgument(new Reference('service_locator.d964744f7278cba85dee823607f8c07f'))), $locator);
$locator = $container->getDefinition((string) $locator['foo:fooAction']->getValues()[0]);
$locator = $container->getDefinition('arguments.foo:fooAction');
$this->assertSame(ServiceLocator::class, $locator->getClass());
$this->assertFalse($locator->isPublic());
$this->assertTrue($locator->isAutowired());
@ -155,7 +154,7 @@ class RegisterControllerArgumentLocatorsPassTest extends TestCase
public function testExplicitArgument()
{
$container = new ContainerBuilder();
$container->register('argument_resolver.service')->addArgument(array());
$resolver = $container->register('argument_resolver.service')->addArgument(array());
$container->register('foo', RegisterTestController::class)
->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar', 'id' => 'bar'))
@ -165,7 +164,8 @@ class RegisterControllerArgumentLocatorsPassTest extends TestCase
$pass = new RegisterControllerArgumentLocatorsPass();
$pass->process($container);
$locator = $container->getDefinition('arguments.foo:fooAction');
$locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
$locator = $container->getDefinition((string) $locator['foo:fooAction']->getValues()[0]);
$this->assertFalse($locator->isAutowired());
$expected = array('bar' => new ServiceClosureArgument(new TypedReference('bar', 'stdClass', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false)));
@ -175,7 +175,7 @@ class RegisterControllerArgumentLocatorsPassTest extends TestCase
public function testOptionalArgument()
{
$container = new ContainerBuilder();
$container->register('argument_resolver.service')->addArgument(array());
$resolver = $container->register('argument_resolver.service')->addArgument(array());
$container->register('foo', RegisterTestController::class)
->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar', 'id' => '?bar'))
@ -184,30 +184,12 @@ class RegisterControllerArgumentLocatorsPassTest extends TestCase
$pass = new RegisterControllerArgumentLocatorsPass();
$pass->process($container);
$locator = $container->getDefinition('arguments.foo:fooAction');
$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)));
$this->assertEquals($expected, $locator->getArgument(0));
}
public function testSameIdClass()
{
$container = new ContainerBuilder();
$resolver = $container->register('argument_resolver.service')->addArgument(array());
$container->register(RegisterTestController::class, RegisterTestController::class)
->addTag('controller.service_arguments')
;
$pass = new RegisterControllerArgumentLocatorsPass();
$pass->process($container);
$expected = array(
RegisterTestController::class.':fooAction' => new ServiceClosureArgument(new Reference('arguments.'.RegisterTestController::class.':fooAction')),
RegisterTestController::class.'::fooAction' => new ServiceClosureArgument(new Reference('arguments.'.RegisterTestController::class.':fooAction')),
);
$this->assertEquals($expected, $resolver->getArgument(0)->getArgument(0));
}
}
class RegisterTestController

View File

@ -24,7 +24,7 @@ class RemoveEmptyControllerArgumentLocatorsPassTest extends TestCase
public function testProcess()
{
$container = new ContainerBuilder();
$container->register('argument_resolver.service')->addArgument(array());
$resolver = $container->register('argument_resolver.service')->addArgument(array());
$container->register('stdClass', 'stdClass');
$container->register(parent::class, 'stdClass');
@ -35,33 +35,49 @@ class RemoveEmptyControllerArgumentLocatorsPassTest extends TestCase
$pass = new RegisterControllerArgumentLocatorsPass();
$pass->process($container);
$this->assertCount(2, $container->getDefinition('arguments.c1:fooAction')->getArgument(0));
$this->assertCount(1, $container->getDefinition('arguments.c2:setTestCase')->getArgument(0));
$this->assertCount(1, $container->getDefinition('arguments.c2:fooAction')->getArgument(0));
$controllers = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
$pass = new ResolveInvalidReferencesPass();
$pass->process($container);
$this->assertCount(2, $container->getDefinition((string) $controllers['c1:fooAction']->getValues()[0])->getArgument(0));
$this->assertCount(1, $container->getDefinition((string) $controllers['c2:setTestCase']->getValues()[0])->getArgument(0));
$this->assertCount(1, $container->getDefinition((string) $controllers['c2:fooAction']->getValues()[0])->getArgument(0));
$this->assertCount(1, $container->getDefinition('arguments.c2:setTestCase')->getArgument(0));
$this->assertSame(array(), $container->getDefinition('arguments.c2:fooAction')->getArgument(0));
(new ResolveInvalidReferencesPass())->process($container);
$pass = new RemoveEmptyControllerArgumentLocatorsPass();
$pass->process($container);
$this->assertCount(1, $container->getDefinition((string) $controllers['c2:setTestCase']->getValues()[0])->getArgument(0));
$this->assertSame(array(), $container->getDefinition((string) $controllers['c2:fooAction']->getValues()[0])->getArgument(0));
$this->assertFalse($container->hasDefinition('arguments.c2:setTestCase'));
$this->assertFalse($container->hasDefinition('arguments.c2:fooAction'));
(new RemoveEmptyControllerArgumentLocatorsPass())->process($container);
$this->assertCount(1, $container->getDefinition('arguments.c1:fooAction')->getArgument(0));
$this->assertArrayHasKey('bar', $container->getDefinition('arguments.c1:fooAction')->getArgument(0));
$controllers = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
$this->assertSame(array('c1:fooAction'), array_keys($controllers));
$this->assertSame(array('bar'), array_keys($container->getDefinition((string) $controllers['c1:fooAction']->getValues()[0])->getArgument(0)));
$expectedLog = array(
'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.',
'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 method "setTestCase" of service "c2" from controller candidates: the method is called at instantiation, thus cannot be an action.',
);
$this->assertSame($expectedLog, $container->getCompiler()->getLog());
}
$this->assertEquals(array('c1:fooAction' => new ServiceClosureArgument(new Reference('arguments.c1:fooAction'))), $container->getDefinition('argument_resolver.service')->getArgument(0)->getArgument(0));
public function testSameIdClass()
{
$container = new ContainerBuilder();
$resolver = $container->register('argument_resolver.service')->addArgument(array());
$container->register(RegisterTestController::class, RegisterTestController::class)
->addTag('controller.service_arguments')
;
(new RegisterControllerArgumentLocatorsPass())->process($container);
(new RemoveEmptyControllerArgumentLocatorsPass())->process($container);
$expected = array(
RegisterTestController::class.':fooAction' => new ServiceClosureArgument(new Reference('service_locator.37b6201ea2e9e6ade00ab09ae7a6ceb3')),
RegisterTestController::class.'::fooAction' => new ServiceClosureArgument(new Reference('service_locator.37b6201ea2e9e6ade00ab09ae7a6ceb3')),
);
$this->assertEquals($expected, $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0));
}
}

View File

@ -11,12 +11,10 @@
namespace Symfony\Component\Validator\DependencyInjection;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceLocator;
/**
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
@ -48,15 +46,15 @@ class AddConstraintValidatorsPass implements CompilerPassInterface
}
if (isset($attributes[0]['alias'])) {
$validators[$attributes[0]['alias']] = new ServiceClosureArgument(new Reference($id));
$validators[$attributes[0]['alias']] = new Reference($id);
}
$validators[$definition->getClass()] = new ServiceClosureArgument(new Reference($id));
$validators[$definition->getClass()] = new Reference($id);
}
$container
->getDefinition('validator.validator_factory')
->replaceArgument(0, (new Definition(ServiceLocator::class, array($validators)))->addTag('container.service_locator'))
->replaceArgument(0, ServiceLocatorTagPass::register($container, $validators))
;
}
}

View File

@ -38,11 +38,12 @@ class AddConstraintValidatorsPassTest extends TestCase
$addConstraintValidatorsPass = new AddConstraintValidatorsPass();
$addConstraintValidatorsPass->process($container);
$this->assertEquals((new Definition(ServiceLocator::class, array(array(
$expected = (new Definition(ServiceLocator::class, array(array(
Validator1::class => new ServiceClosureArgument(new Reference('my_constraint_validator_service1')),
'my_constraint_validator_alias1' => new ServiceClosureArgument(new Reference('my_constraint_validator_service1')),
Validator2::class => new ServiceClosureArgument(new Reference('my_constraint_validator_service2')),
))))->addTag('container.service_locator'), $validatorFactory->getArgument(0));
))))->addTag('container.service_locator')->setPublic(false);
$this->assertEquals($expected, $container->getDefinition((string) $validatorFactory->getArgument(0)));
}
public function testThatCompilerPassIsIgnoredIfThereIsNoConstraintValidatorFactoryDefinition()