Skip to content

Commit 7adba7f

Browse files
author
Iltar van der Berg
committed
Extracting arg resolving from ControllerResolver
1 parent 5243d4e commit 7adba7f

12 files changed

+356
-55
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ CHANGELOG
44
3.1.0
55
-----
66
* deprecated passing objects as URI attributes to the ESI and SSI renderers
7+
* Added an `ArgumentResolver` with `getArguments()` and the respective interface `ArgumentResolverInterface`
8+
* Deprecated `ControllerResolver::getArguments()`, which uses the `ArgumentResolver` as BC layer by extending it
9+
* The `HttpKernel` now accepts an additional argument for an `ArgumentResolver`
710

811
3.0.0
912
-----

Controller/ArgumentResolver.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Controller;
13+
14+
use Symfony\Component\HttpFoundation\Request;
15+
16+
/**
17+
* Responsible for the creation of the action arguments.
18+
*
19+
* @author Fabien Potencier <fabien@symfony.com>
20+
*/
21+
class ArgumentResolver implements ArgumentResolverInterface
22+
{
23+
/**
24+
* {@inheritdoc}
25+
*/
26+
public function getArguments(Request $request, $controller)
27+
{
28+
if (is_array($controller)) {
29+
$r = new \ReflectionMethod($controller[0], $controller[1]);
30+
} elseif (is_object($controller) && !$controller instanceof \Closure) {
31+
$r = new \ReflectionObject($controller);
32+
$r = $r->getMethod('__invoke');
33+
} else {
34+
$r = new \ReflectionFunction($controller);
35+
}
36+
37+
return $this->doGetArguments($request, $controller, $r->getParameters());
38+
}
39+
40+
protected function doGetArguments(Request $request, $controller, array $parameters)
41+
{
42+
$attributes = $request->attributes->all();
43+
$arguments = array();
44+
foreach ($parameters as $param) {
45+
if (array_key_exists($param->name, $attributes)) {
46+
if (PHP_VERSION_ID >= 50600 && $param->isVariadic() && is_array($attributes[$param->name])) {
47+
$arguments = array_merge($arguments, array_values($attributes[$param->name]));
48+
} else {
49+
$arguments[] = $attributes[$param->name];
50+
}
51+
} elseif ($param->getClass() && $param->getClass()->isInstance($request)) {
52+
$arguments[] = $request;
53+
} elseif ($param->isDefaultValueAvailable()) {
54+
$arguments[] = $param->getDefaultValue();
55+
} else {
56+
if (is_array($controller)) {
57+
$repr = sprintf('%s::%s()', get_class($controller[0]), $controller[1]);
58+
} elseif (is_object($controller)) {
59+
$repr = get_class($controller);
60+
} else {
61+
$repr = $controller;
62+
}
63+
64+
throw new \RuntimeException(sprintf('Controller "%s" requires that you provide a value for the "$%s" argument (because there is no default value or because there is a non optional argument after this one).', $repr, $param->name));
65+
}
66+
}
67+
68+
return $arguments;
69+
}
70+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Controller;
13+
14+
use Symfony\Component\HttpFoundation\Request;
15+
16+
/**
17+
* An ArgumentResolverInterface implementation knows how to determine the
18+
* arguments for a specific action.
19+
*
20+
* @author Fabien Potencier <fabien@symfony.com>
21+
*/
22+
interface ArgumentResolverInterface
23+
{
24+
/**
25+
* Returns the arguments to pass to the controller.
26+
*
27+
* @param Request $request A Request instance
28+
* @param callable $controller A PHP callable
29+
*
30+
* @return array An array of arguments to pass to the controller
31+
*
32+
* @throws \RuntimeException When value for argument given is not provided
33+
*/
34+
public function getArguments(Request $request, $controller);
35+
}

Controller/ControllerResolver.php

Lines changed: 10 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*
2424
* @author Fabien Potencier <fabien@symfony.com>
2525
*/
26-
class ControllerResolver implements ControllerResolverInterface
26+
class ControllerResolver extends ArgumentResolver implements ControllerResolverInterface
2727
{
2828
private $logger;
2929

@@ -84,50 +84,24 @@ public function getController(Request $request)
8484

8585
/**
8686
* {@inheritdoc}
87+
*
88+
* @deprecated this method is deprecated as of 3.1 and will be removed in 4.0. Implement the ArgumentResolverInterface or extend the ArgumentResolver instead.
8789
*/
8890
public function getArguments(Request $request, $controller)
8991
{
90-
if (is_array($controller)) {
91-
$r = new \ReflectionMethod($controller[0], $controller[1]);
92-
} elseif (is_object($controller) && !$controller instanceof \Closure) {
93-
$r = new \ReflectionObject($controller);
94-
$r = $r->getMethod('__invoke');
95-
} else {
96-
$r = new \ReflectionFunction($controller);
97-
}
92+
@trigger_error(sprintf('%s is deprecated as of 3.1 and will be removed in 4.0. Implement the %s or extend the %s and inject it in the HttpKernel instead.', __METHOD__, ArgumentResolverInterface::class, ArgumentResolver::class), E_USER_DEPRECATED);
9893

99-
return $this->doGetArguments($request, $controller, $r->getParameters());
94+
return parent::getArguments($request, $controller);
10095
}
10196

97+
/**
98+
* @deprecated this method is deprecated as of 3.1 and will be removed in 4.0. Implement the ArgumentResolverInterface or extend the ArgumentResolver instead.
99+
*/
102100
protected function doGetArguments(Request $request, $controller, array $parameters)
103101
{
104-
$attributes = $request->attributes->all();
105-
$arguments = array();
106-
foreach ($parameters as $param) {
107-
if (array_key_exists($param->name, $attributes)) {
108-
if (PHP_VERSION_ID >= 50600 && $param->isVariadic() && is_array($attributes[$param->name])) {
109-
$arguments = array_merge($arguments, array_values($attributes[$param->name]));
110-
} else {
111-
$arguments[] = $attributes[$param->name];
112-
}
113-
} elseif ($param->getClass() && $param->getClass()->isInstance($request)) {
114-
$arguments[] = $request;
115-
} elseif ($param->isDefaultValueAvailable()) {
116-
$arguments[] = $param->getDefaultValue();
117-
} else {
118-
if (is_array($controller)) {
119-
$repr = sprintf('%s::%s()', get_class($controller[0]), $controller[1]);
120-
} elseif (is_object($controller)) {
121-
$repr = get_class($controller);
122-
} else {
123-
$repr = $controller;
124-
}
125-
126-
throw new \RuntimeException(sprintf('Controller "%s" requires that you provide a value for the "$%s" argument (because there is no default value or because there is a non optional argument after this one).', $repr, $param->name));
127-
}
128-
}
102+
@trigger_error(sprintf('%s is deprecated as of 3.1 and will be removed in 4.0. Implement the %s or extend the %s and inject it in the HttpKernel instead.', __METHOD__, ArgumentResolverInterface::class, ArgumentResolver::class), E_USER_DEPRECATED);
129103

130-
return $arguments;
104+
return parent::doGetArguments($request, $controller, $parameters);
131105
}
132106

133107
/**

Controller/ControllerResolverInterface.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ public function getController(Request $request);
5252
* @return array An array of arguments to pass to the controller
5353
*
5454
* @throws \RuntimeException When value for argument given is not provided
55+
*
56+
* @deprecated This method is deprecated as of 3.1 and will be removed in 4.0. Please use the {@see ArgumentResolverInterface} instead.
5557
*/
5658
public function getArguments(Request $request, $controller);
5759
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Controller;
13+
14+
use Symfony\Component\Stopwatch\Stopwatch;
15+
use Symfony\Component\HttpFoundation\Request;
16+
17+
/**
18+
* @author Fabien Potencier <fabien@symfony.com>
19+
*/
20+
class TraceableArgumentResolver implements ArgumentResolverInterface
21+
{
22+
private $resolver;
23+
private $stopwatch;
24+
25+
public function __construct(ArgumentResolverInterface $resolver, Stopwatch $stopwatch)
26+
{
27+
$this->resolver = $resolver;
28+
$this->stopwatch = $stopwatch;
29+
}
30+
31+
/**
32+
* {@inheritdoc}
33+
*/
34+
public function getArguments(Request $request, $controller)
35+
{
36+
$e = $this->stopwatch->start('controller.get_arguments');
37+
38+
$ret = $this->resolver->getArguments($request, $controller);
39+
40+
$e->stop();
41+
42+
return $ret;
43+
}
44+
}

Controller/TraceableControllerResolver.php

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,28 @@
1919
*
2020
* @author Fabien Potencier <fabien@symfony.com>
2121
*/
22-
class TraceableControllerResolver implements ControllerResolverInterface
22+
class TraceableControllerResolver implements ControllerResolverInterface, ArgumentResolverInterface
2323
{
2424
private $resolver;
2525
private $stopwatch;
26+
private $argumentResolver;
2627

2728
/**
2829
* Constructor.
2930
*
30-
* @param ControllerResolverInterface $resolver A ControllerResolverInterface instance
31-
* @param Stopwatch $stopwatch A Stopwatch instance
31+
* @param ControllerResolverInterface $resolver A ControllerResolverInterface instance
32+
* @param Stopwatch $stopwatch A Stopwatch instance
33+
* @param ArgumentResolverInterface $argumentResolver Only required for BC
3234
*/
33-
public function __construct(ControllerResolverInterface $resolver, Stopwatch $stopwatch)
35+
public function __construct(ControllerResolverInterface $resolver, Stopwatch $stopwatch, ArgumentResolverInterface $argumentResolver = null)
3436
{
3537
$this->resolver = $resolver;
3638
$this->stopwatch = $stopwatch;
39+
$this->argumentResolver = $argumentResolver;
40+
41+
if (null === $this->argumentResolver) {
42+
$this->argumentResolver = $resolver;
43+
}
3744
}
3845

3946
/**
@@ -52,12 +59,20 @@ public function getController(Request $request)
5259

5360
/**
5461
* {@inheritdoc}
62+
*
63+
* @deprecated This method is deprecated as of 3.1 and will be removed in 4.0.
5564
*/
5665
public function getArguments(Request $request, $controller)
5766
{
67+
@trigger_error(sprintf('This %s method is deprecated as of 3.1 and will be removed in 4.0. Please use the %s instead.', __METHOD__, TraceableArgumentResolver::class), E_USER_DEPRECATED);
68+
69+
if ($this->argumentResolver instanceof TraceableArgumentResolver) {
70+
return $this->argumentResolver->getArguments($request, $controller);
71+
}
72+
5873
$e = $this->stopwatch->start('controller.get_arguments');
5974

60-
$ret = $this->resolver->getArguments($request, $controller);
75+
$ret = $this->argumentResolver->getArguments($request, $controller);
6176

6277
$e->stop();
6378

HttpKernel.php

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\HttpKernel;
1313

14+
use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
15+
use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface;
1416
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
1517
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
1618
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
@@ -36,19 +38,28 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
3638
protected $dispatcher;
3739
protected $resolver;
3840
protected $requestStack;
41+
private $argumentResolver;
3942

4043
/**
4144
* Constructor.
4245
*
43-
* @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
44-
* @param ControllerResolverInterface $resolver A ControllerResolverInterface instance
45-
* @param RequestStack $requestStack A stack for master/sub requests
46+
* @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
47+
* @param ControllerResolverInterface $resolver A ControllerResolverInterface instance
48+
* @param RequestStack $requestStack A stack for master/sub requests
49+
* @param ArgumentResolverInterface $argumentResolver An ArgumentResolverInterface instance
4650
*/
47-
public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver, RequestStack $requestStack = null)
51+
public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver, RequestStack $requestStack = null, ArgumentResolverInterface $argumentResolver = null)
4852
{
4953
$this->dispatcher = $dispatcher;
5054
$this->resolver = $resolver;
5155
$this->requestStack = $requestStack ?: new RequestStack();
56+
$this->argumentResolver = $argumentResolver;
57+
58+
if (null === $this->argumentResolver) {
59+
@trigger_error(sprintf('As of 3.1 an %s is used to resolve arguments. In 4.0 the $argumentResolver becomes mandatory and the %s can no longer be used to resolve arguments.', ArgumentResolverInterface::class, ControllerResolverInterface::class), E_USER_DEPRECATED);
60+
// fallback in case of deprecations
61+
$this->argumentResolver = $resolver;
62+
}
5263
}
5364

5465
/**
@@ -133,7 +144,7 @@ private function handleRaw(Request $request, $type = self::MASTER_REQUEST)
133144
$controller = $event->getController();
134145

135146
// controller arguments
136-
$arguments = $this->resolver->getArguments($request, $controller);
147+
$arguments = $this->argumentResolver->getArguments($request, $controller);
137148

138149
// call controller
139150
$response = call_user_func_array($controller, $arguments);

0 commit comments

Comments
 (0)