[FrameworkBundle] Add new "controller.service_arguments" tag to inject services into actions
This commit is contained in:
parent
3023e4b707
commit
9c6e672780
@ -4,6 +4,7 @@ CHANGELOG
|
||||
3.3.0
|
||||
-----
|
||||
|
||||
* Added support for the `controller.service_arguments` tag, for injecting services into controllers' actions
|
||||
* Deprecated `cache:clear` with warmup (always call it with `--no-warmup`)
|
||||
* Changed default configuration for
|
||||
assets/forms/validation/translation/serialization/csrf from `canBeEnabled()` to
|
||||
|
@ -25,6 +25,7 @@ class UnusedTagsPass implements CompilerPassInterface
|
||||
'console.command',
|
||||
'container.service_locator',
|
||||
'container.service_subscriber',
|
||||
'controller.service_arguments',
|
||||
'config_cache.resource_checker',
|
||||
'data_collector',
|
||||
'form.type',
|
||||
|
@ -35,6 +35,8 @@ use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ValidateWorkflow
|
||||
use Symfony\Component\Config\DependencyInjection\ConfigCachePass;
|
||||
use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\ControllerArgumentValueResolverPass;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\RegisterControllerArgumentLocatorsPass;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\RemoveEmptyControllerArgumentLocatorsPass;
|
||||
use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass;
|
||||
use Symfony\Component\Routing\DependencyInjection\RoutingResolverPass;
|
||||
use Symfony\Component\Serializer\DependencyInjection\SerializerPass;
|
||||
@ -76,6 +78,8 @@ class FrameworkBundle extends Bundle
|
||||
{
|
||||
parent::build($container);
|
||||
|
||||
$container->addCompilerPass(new RegisterControllerArgumentLocatorsPass());
|
||||
$container->addCompilerPass(new RemoveEmptyControllerArgumentLocatorsPass(), PassConfig::TYPE_BEFORE_REMOVING);
|
||||
$container->addCompilerPass(new RoutingResolverPass());
|
||||
$container->addCompilerPass(new ProfilerPass());
|
||||
// must be registered before removing private services as some might be listeners/subscribers
|
||||
|
@ -36,6 +36,11 @@
|
||||
<tag name="controller.argument_value_resolver" priority="50" />
|
||||
</service>
|
||||
|
||||
<service id="argument_resolver.service" class="Symfony\Component\HttpKernel\Controller\ArgumentResolver\ServiceValueResolver" public="false">
|
||||
<tag name="controller.argument_value_resolver" priority="-50" />
|
||||
<argument />
|
||||
</service>
|
||||
|
||||
<service id="argument_resolver.default" class="Symfony\Component\HttpKernel\Controller\ArgumentResolver\DefaultValueResolver" public="false">
|
||||
<tag name="controller.argument_value_resolver" priority="-100" />
|
||||
</service>
|
||||
|
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Controller\ArgumentResolver;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
|
||||
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
|
||||
|
||||
/**
|
||||
* Yields a service keyed by _controller and argument name.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
final class ServiceValueResolver implements ArgumentValueResolverInterface
|
||||
{
|
||||
private $container;
|
||||
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(Request $request, ArgumentMetadata $argument)
|
||||
{
|
||||
return is_string($controller = $request->attributes->get('_controller')) && $this->container->has($controller) && $this->container->get($controller)->has($argument->getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resolve(Request $request, ArgumentMetadata $argument)
|
||||
{
|
||||
yield $this->container->get($request->attributes->get('_controller'))->get($argument->getName());
|
||||
}
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
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\InheritanceProxyHelper;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\TypedReference;
|
||||
|
||||
/**
|
||||
* Creates the service-locators required by ServiceArgumentValueResolver.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface
|
||||
{
|
||||
private $resolverServiceId;
|
||||
private $controllerTag;
|
||||
|
||||
public function __construct($resolverServiceId = 'argument_resolver.service', $controllerTag = 'controller.service_arguments')
|
||||
{
|
||||
$this->resolverServiceId = $resolverServiceId;
|
||||
$this->controllerTag = $controllerTag;
|
||||
}
|
||||
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (false === $container->hasDefinition($this->resolverServiceId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parameterBag = $container->getParameterBag();
|
||||
$controllers = array();
|
||||
|
||||
foreach ($container->findTaggedServiceIds($this->controllerTag) as $id => $tags) {
|
||||
$def = $container->getDefinition($id);
|
||||
|
||||
if ($def->isAbstract()) {
|
||||
continue;
|
||||
}
|
||||
$class = $def->getClass();
|
||||
$isAutowired = $def->isAutowired();
|
||||
|
||||
// resolve service class, taking parent definitions into account
|
||||
while (!$class && $def instanceof ChildDefinition) {
|
||||
$def = $container->findDefinition($def->getParent());
|
||||
$class = $def->getClass();
|
||||
}
|
||||
$class = $parameterBag->resolveValue($class);
|
||||
|
||||
if (!$r = $container->getReflectionClass($class)) {
|
||||
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
|
||||
}
|
||||
|
||||
// get regular public methods
|
||||
$methods = array();
|
||||
$arguments = array();
|
||||
foreach ($r->getMethods(\ReflectionMethod::IS_PUBLIC) as $r) {
|
||||
if (!$r->isConstructor() && !$r->isDestructor() && !$r->isAbstract()) {
|
||||
$methods[strtolower($r->name)] = array($r, $r->getParameters());
|
||||
}
|
||||
}
|
||||
|
||||
// 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'])) {
|
||||
continue;
|
||||
}
|
||||
foreach (array('action', 'argument', 'id') as $k) {
|
||||
if (!isset($attributes[$k][0])) {
|
||||
throw new InvalidArgumentException(sprintf('Missing "%s" attribute on tag "%s" %s for service "%s".', $k, $this->controllerTag, json_encode($attributes, JSON_UNESCAPED_UNICODE), $id));
|
||||
}
|
||||
}
|
||||
if (!isset($methods[$action = strtolower($attributes['action'])])) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid "action" attribute on tag "%s" for service "%s": no public "%s()" method found on class "%s".', $this->controllerTag, $id, $attributes['action'], $class));
|
||||
}
|
||||
list($r, $parameters) = $methods[$action];
|
||||
$found = false;
|
||||
|
||||
foreach ($parameters as $p) {
|
||||
if ($attributes['argument'] === $p->name) {
|
||||
if (!isset($arguments[$r->name][$p->name])) {
|
||||
$arguments[$r->name][$p->name] = $attributes['id'];
|
||||
}
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$found) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid "%s" tag for service "%s": method "%s()" has no "%s" argument on class "%s".', $this->controllerTag, $id, $r->name, $attributes['argument'], $class));
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($methods as list($r, $parameters)) {
|
||||
// create a per-method map of argument-names to service/type-references
|
||||
$args = array();
|
||||
foreach ($parameters as $p) {
|
||||
$type = $target = InheritanceProxyHelper::getTypeHint($r, $p, true);
|
||||
$invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
|
||||
|
||||
if (isset($arguments[$r->name][$p->name])) {
|
||||
$target = $arguments[$r->name][$p->name];
|
||||
if ('?' !== $target[0]) {
|
||||
$invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
|
||||
} elseif ('' === $target = (string) substr($target, 1)) {
|
||||
throw new InvalidArgumentException(sprintf('A "%s" tag must have non-empty "id" attributes for service "%s".', $this->controllerTag, $id));
|
||||
} elseif ($p->allowsNull() && !$p->isOptional()) {
|
||||
$invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE;
|
||||
}
|
||||
} elseif (!$type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$args[$p->name] = new ServiceClosureArgument($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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$container->getDefinition($this->resolverServiceId)
|
||||
->replaceArgument(0, (new Definition(ServiceLocator::class, array($controllers)))->addTag('container.service_locator'));
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* Removes empty service-locators registered for ServiceArgumentValueResolver.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class RemoveEmptyControllerArgumentLocatorsPass implements CompilerPassInterface
|
||||
{
|
||||
private $resolverServiceId;
|
||||
|
||||
public function __construct($resolverServiceId = 'argument_resolver.service')
|
||||
{
|
||||
$this->resolverServiceId = $resolverServiceId;
|
||||
}
|
||||
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (false === $container->hasDefinition($this->resolverServiceId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$serviceResolver = $container->getDefinition($this->resolverServiceId);
|
||||
$controllers = $serviceResolver->getArgument(0)->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];
|
||||
|
||||
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?' : '');
|
||||
} else {
|
||||
// any methods listed for call-at-instantiation cannot be actions
|
||||
$reason = false;
|
||||
foreach ($container->getDefinition($service)->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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$reason) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$container->removeDefinition($id);
|
||||
unset($controllers[$service.':'.$action]);
|
||||
if ($service === $class) {
|
||||
unset($controllers[$service.'::'.$action]);
|
||||
}
|
||||
$container->log($this, $reason);
|
||||
}
|
||||
|
||||
$serviceResolver->getArgument(0)->replaceArgument(0, $controllers);
|
||||
}
|
||||
}
|
@ -0,0 +1,226 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Tests\DependencyInjection;
|
||||
|
||||
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;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\RegisterControllerArgumentLocatorsPass;
|
||||
|
||||
class RegisterControllerArgumentLocatorsPassTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage Class "Symfony\Component\HttpKernel\Tests\DependencyInjection\NotFound" used for service "foo" cannot be found.
|
||||
*/
|
||||
public function testInvalidClass()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('argument_resolver.service')->addArgument(array());
|
||||
|
||||
$container->register('foo', NotFound::class)
|
||||
->addTag('controller.service_arguments')
|
||||
;
|
||||
|
||||
$pass = new RegisterControllerArgumentLocatorsPass();
|
||||
$pass->process($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage Missing "action" attribute on tag "controller.service_arguments" {"argument":"bar"} for service "foo".
|
||||
*/
|
||||
public function testNoAction()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('argument_resolver.service')->addArgument(array());
|
||||
|
||||
$container->register('foo', RegisterTestController::class)
|
||||
->addTag('controller.service_arguments', array('argument' => 'bar'))
|
||||
;
|
||||
|
||||
$pass = new RegisterControllerArgumentLocatorsPass();
|
||||
$pass->process($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage Missing "argument" attribute on tag "controller.service_arguments" {"action":"fooAction"} for service "foo".
|
||||
*/
|
||||
public function testNoArgument()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('argument_resolver.service')->addArgument(array());
|
||||
|
||||
$container->register('foo', RegisterTestController::class)
|
||||
->addTag('controller.service_arguments', array('action' => 'fooAction'))
|
||||
;
|
||||
|
||||
$pass = new RegisterControllerArgumentLocatorsPass();
|
||||
$pass->process($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage Missing "id" attribute on tag "controller.service_arguments" {"action":"fooAction","argument":"bar"} for service "foo".
|
||||
*/
|
||||
public function testNoService()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('argument_resolver.service')->addArgument(array());
|
||||
|
||||
$container->register('foo', RegisterTestController::class)
|
||||
->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar'))
|
||||
;
|
||||
|
||||
$pass = new RegisterControllerArgumentLocatorsPass();
|
||||
$pass->process($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage Invalid "action" attribute on tag "controller.service_arguments" for service "foo": no public "barAction()" method found on class "Symfony\Component\HttpKernel\Tests\DependencyInjection\RegisterTestController".
|
||||
*/
|
||||
public function testInvalidMethod()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('argument_resolver.service')->addArgument(array());
|
||||
|
||||
$container->register('foo', RegisterTestController::class)
|
||||
->addTag('controller.service_arguments', array('action' => 'barAction', 'argument' => 'bar', 'id' => 'bar_service'))
|
||||
;
|
||||
|
||||
$pass = new RegisterControllerArgumentLocatorsPass();
|
||||
$pass->process($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage Invalid "controller.service_arguments" tag for service "foo": method "fooAction()" has no "baz" argument on class "Symfony\Component\HttpKernel\Tests\DependencyInjection\RegisterTestController".
|
||||
*/
|
||||
public function testInvalidArgument()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('argument_resolver.service')->addArgument(array());
|
||||
|
||||
$container->register('foo', RegisterTestController::class)
|
||||
->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'baz', 'id' => 'bar'))
|
||||
;
|
||||
|
||||
$pass = new RegisterControllerArgumentLocatorsPass();
|
||||
$pass->process($container);
|
||||
}
|
||||
|
||||
public function testAllActions()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('argument_resolver.service')->addArgument(array());
|
||||
|
||||
$container->register('foo', RegisterTestController::class)
|
||||
->setAutowired(true)
|
||||
->addTag('controller.service_arguments')
|
||||
;
|
||||
|
||||
$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('arguments.foo:fooAction');
|
||||
$this->assertSame(ServiceLocator::class, $locator->getClass());
|
||||
$this->assertFalse($locator->isPublic());
|
||||
$this->assertTrue($locator->isAutowired());
|
||||
|
||||
$expected = array('bar' => new ServiceClosureArgument(new TypedReference('stdClass', 'stdClass', ContainerInterface::IGNORE_ON_INVALID_REFERENCE, false)));
|
||||
$this->assertEquals($expected, $locator->getArgument(0));
|
||||
}
|
||||
|
||||
public function testExplicitArgument()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('argument_resolver.service')->addArgument(array());
|
||||
|
||||
$container->register('foo', RegisterTestController::class)
|
||||
->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar', 'id' => 'bar'))
|
||||
->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar', 'id' => 'baz')) // should be ignored, the first wins
|
||||
;
|
||||
|
||||
$pass = new RegisterControllerArgumentLocatorsPass();
|
||||
$pass->process($container);
|
||||
|
||||
$locator = $container->getDefinition('arguments.foo:fooAction');
|
||||
$this->assertFalse($locator->isAutowired());
|
||||
|
||||
$expected = array('bar' => new ServiceClosureArgument(new TypedReference('bar', 'stdClass', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false)));
|
||||
$this->assertEquals($expected, $locator->getArgument(0));
|
||||
}
|
||||
|
||||
public function testOptionalArgument()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('argument_resolver.service')->addArgument(array());
|
||||
|
||||
$container->register('foo', RegisterTestController::class)
|
||||
->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar', 'id' => '?bar'))
|
||||
;
|
||||
|
||||
$pass = new RegisterControllerArgumentLocatorsPass();
|
||||
$pass->process($container);
|
||||
|
||||
$locator = $container->getDefinition('arguments.foo:fooAction');
|
||||
|
||||
$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
|
||||
{
|
||||
public function __construct(\stdClass $bar)
|
||||
{
|
||||
}
|
||||
|
||||
public function fooAction(\stdClass $bar)
|
||||
{
|
||||
}
|
||||
|
||||
protected function barAction(\stdClass $bar)
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Tests\DependencyInjection;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ResolveInvalidReferencesPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\RegisterControllerArgumentLocatorsPass;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\RemoveEmptyControllerArgumentLocatorsPass;
|
||||
|
||||
class RemoveEmptyControllerArgumentLocatorsPassTest extends TestCase
|
||||
{
|
||||
public function testProcess()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('argument_resolver.service')->addArgument(array());
|
||||
|
||||
$container->register('stdClass', 'stdClass');
|
||||
$container->register(parent::class, 'stdClass');
|
||||
$container->register('c1', RemoveTestController1::class)->addTag('controller.service_arguments');
|
||||
$container->register('c2', RemoveTestController2::class)->addTag('controller.service_arguments')
|
||||
->addMethodCall('setTestCase', array(new Reference('c1')));
|
||||
|
||||
$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));
|
||||
|
||||
$pass = new ResolveInvalidReferencesPass();
|
||||
$pass->process($container);
|
||||
|
||||
$this->assertCount(1, $container->getDefinition('arguments.c2:setTestCase')->getArgument(0));
|
||||
$this->assertSame(array(), $container->getDefinition('arguments.c2:fooAction')->getArgument(0));
|
||||
|
||||
$pass = new RemoveEmptyControllerArgumentLocatorsPass();
|
||||
$pass->process($container);
|
||||
|
||||
$this->assertFalse($container->hasDefinition('arguments.c2:setTestCase'));
|
||||
$this->assertFalse($container->hasDefinition('arguments.c2:fooAction'));
|
||||
|
||||
$this->assertCount(1, $container->getDefinition('arguments.c1:fooAction')->getArgument(0));
|
||||
$this->assertArrayHasKey('bar', $container->getDefinition('arguments.c1:fooAction')->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?',
|
||||
);
|
||||
|
||||
$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));
|
||||
}
|
||||
}
|
||||
|
||||
class RemoveTestController1
|
||||
{
|
||||
public function fooAction(\stdClass $bar, NotFound $baz)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class RemoveTestController2
|
||||
{
|
||||
public function setTestCase(TestCase $test)
|
||||
{
|
||||
}
|
||||
|
||||
public function fooAction(NotFound $bar)
|
||||
{
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user