Skip to content

Commit a2f8c32

Browse files
bug symfony#24972 [HttpKernel] Fix service arg resolver for controllers as array callables (sroze, nicolas-grekas)
This PR was merged into the 3.4 branch. Discussion ---------- [HttpKernel] Fix service arg resolver for controllers as array callables | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | symfony#24970 | License | MIT | Doc PR | ø Replacing symfony#24970 as I can't push tests directly to @nicolas-grekas' PR. > As spotted today during a Symfony 4 workshop at SymfonyCon Cluj, setting a controller as an array [SomeController::class, 'helloAction'] works, it is defined as a service, BUT the actions don't get the services as arguments. This is fixing it. Commits ------- fc3d3bb [HttpKernel] Fix service arg resolver for controllers as array callables a9e9f36 Add service value resolver tests Prove that the service value resolver will not work with the array notation
2 parents 3398c4b + fc3d3bb commit a2f8c32

File tree

2 files changed

+105
-2
lines changed

2 files changed

+105
-2
lines changed

src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/ServiceValueResolver.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,24 @@ public function __construct(ContainerInterface $container)
3535
*/
3636
public function supports(Request $request, ArgumentMetadata $argument)
3737
{
38-
return is_string($controller = $request->attributes->get('_controller')) && $this->container->has($controller) && $this->container->get($controller)->has($argument->getName());
38+
$controller = $request->attributes->get('_controller');
39+
40+
if (\is_array($controller) && \is_callable($controller, true) && \is_string($controller[0])) {
41+
$controller = $controller[0].'::'.$controller[1];
42+
}
43+
44+
return \is_string($controller) && $this->container->has($controller) && $this->container->get($controller)->has($argument->getName());
3945
}
4046

4147
/**
4248
* {@inheritdoc}
4349
*/
4450
public function resolve(Request $request, ArgumentMetadata $argument)
4551
{
46-
yield $this->container->get($request->attributes->get('_controller'))->get($argument->getName());
52+
if (\is_array($controller = $request->attributes->get('_controller'))) {
53+
$controller = $controller[0].'::'.$controller[1];
54+
}
55+
56+
yield $this->container->get($controller)->get($argument->getName());
4757
}
4858
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpKernel\Tests\Controller\ArgumentResolver;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\DependencyInjection\ServiceLocator;
16+
use Symfony\Component\HttpFoundation\Request;
17+
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\ServiceValueResolver;
18+
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
19+
20+
class ServiceValueResolverTest extends TestCase
21+
{
22+
public function testDoNotSupportWhenControllerDoNotExists()
23+
{
24+
$resolver = new ServiceValueResolver(new ServiceLocator(array()));
25+
$argument = new ArgumentMetadata('dummy', DummyService::class, false, false, null);
26+
$request = $this->requestWithAttributes(array('_controller' => 'my_controller'));
27+
28+
$this->assertFalse($resolver->supports($request, $argument));
29+
}
30+
31+
public function testExistingController()
32+
{
33+
$resolver = new ServiceValueResolver(new ServiceLocator(array(
34+
'App\\Controller\\Mine::method' => function () {
35+
return new ServiceLocator(array(
36+
'dummy' => function () {
37+
return new DummyService();
38+
},
39+
));
40+
},
41+
)));
42+
43+
$request = $this->requestWithAttributes(array('_controller' => 'App\\Controller\\Mine::method'));
44+
$argument = new ArgumentMetadata('dummy', DummyService::class, false, false, null);
45+
46+
$this->assertTrue($resolver->supports($request, $argument));
47+
$this->assertYieldEquals(array(new DummyService()), $resolver->resolve($request, $argument));
48+
}
49+
50+
public function testControllerNameIsAnArray()
51+
{
52+
$resolver = new ServiceValueResolver(new ServiceLocator(array(
53+
'App\\Controller\\Mine::method' => function () {
54+
return new ServiceLocator(array(
55+
'dummy' => function () {
56+
return new DummyService();
57+
},
58+
));
59+
},
60+
)));
61+
62+
$request = $this->requestWithAttributes(array('_controller' => array('App\\Controller\\Mine', 'method')));
63+
$argument = new ArgumentMetadata('dummy', DummyService::class, false, false, null);
64+
65+
$this->assertTrue($resolver->supports($request, $argument));
66+
$this->assertYieldEquals(array(new DummyService()), $resolver->resolve($request, $argument));
67+
}
68+
69+
private function requestWithAttributes(array $attributes)
70+
{
71+
$request = Request::create('/');
72+
73+
foreach ($attributes as $name => $value) {
74+
$request->attributes->set($name, $value);
75+
}
76+
77+
return $request;
78+
}
79+
80+
private function assertYieldEquals(array $expected, \Generator $generator)
81+
{
82+
$args = array();
83+
foreach ($generator as $arg) {
84+
$args[] = $arg;
85+
}
86+
87+
$this->assertEquals($expected, $args);
88+
}
89+
}
90+
91+
class DummyService
92+
{
93+
}

0 commit comments

Comments
 (0)