Skip to content

Commit 24b11e2

Browse files
author
Iltar van der Berg
committed
Documented the ArgumentResolver along the ControllerResolver
1 parent d7724dd commit 24b11e2

File tree

6 files changed

+162
-92
lines changed

6 files changed

+162
-92
lines changed

components/http_kernel/introduction.rst

Lines changed: 80 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ to the events discussed below::
9090
use Symfony\Component\HttpFoundation\Request;
9191
use Symfony\Component\HttpKernel\HttpKernel;
9292
use Symfony\Component\EventDispatcher\EventDispatcher;
93+
use Symfony\Component\HttpFoundation\RequestStack;
94+
use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
9395
use Symfony\Component\HttpKernel\Controller\ControllerResolver;
9496

9597
// create the Request object
@@ -98,10 +100,12 @@ to the events discussed below::
98100
$dispatcher = new EventDispatcher();
99101
// ... add some event listeners
100102

101-
// create your controller resolver
102-
$resolver = new ControllerResolver();
103+
// create your controller and argument resolvers
104+
$controllerResolver = new ControllerResolver();
105+
$argumentResolver = new ArgumentResolver();
106+
103107
// instantiate the kernel
104-
$kernel = new HttpKernel($dispatcher, $resolver);
108+
$kernel = new HttpKernel($dispatcher, $controllerResolver, new RequestStack(), $argumentResolver);
105109

106110
// actually execute the kernel, which turns the request into a response
107111
// by dispatching events, calling a controller, and returning the response
@@ -118,6 +122,14 @@ See ":ref:`http-kernel-working-example`" for a more concrete implementation.
118122
For general information on adding listeners to the events below, see
119123
:ref:`http-kernel-creating-listener`.
120124

125+
126+
.. caution::
127+
128+
As of 3.1 the :class:`Symfony\\Component\\Httpkernel\\HttpKernel` accepts a fourth argument, which
129+
must be an instance of :class:`Symfony\\Component\\Httpkernel\\Controller\\ArgumentResolverInterface`.
130+
In 4.0 this argument will become mandatory and the :class:`Symfony\\Component\\Httpkernel\\HttpKernel`
131+
will no longer be able to fall back to the :class:`Symfony\\Component\\Httpkernel\\Controller\\ControllerResolver`.
132+
121133
.. seealso::
122134

123135
There is a wonderful tutorial series on using the HttpKernel component and
@@ -225,13 +237,21 @@ This implementation is explained more in the sidebar below::
225237
public function getArguments(Request $request, $controller);
226238
}
227239

240+
.. caution::
241+
242+
The ``getArguments()`` method in the :class:`Symfony\\Component\\Httpkernel\\Controller\\ControllerResolver`
243+
and respective interface :class:`Symfony\\Component\\Httpkernel\\Controller\\ControllerResolverInterface`
244+
are deprecated as of 3.1 and will be removed in 4.0. You can use the
245+
:class:`Symfony\\Component\\Httpkernel\\Controller\\ArgumentResolver` which uses the
246+
:class:`Symfony\\Component\\Httpkernel\\Controller\\ArgumentResolverInterface` instead.
247+
228248
Internally, the ``HttpKernel::handle`` method first calls
229249
:method:`Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface::getController`
230250
on the controller resolver. This method is passed the ``Request`` and is responsible
231251
for somehow determining and returning a PHP callable (the controller) based
232252
on the request's information.
233253

234-
The second method, :method:`Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface::getArguments`,
254+
The second method, :method:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolverInterface::getArguments`,
235255
will be called after another event - ``kernel.controller`` - is dispatched.
236256

237257
.. sidebar:: Resolving the Controller in the Symfony Framework
@@ -310,11 +330,11 @@ on the event object that's passed to listeners on this event.
310330
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
311331

312332
Next, ``HttpKernel::handle`` calls
313-
:method:`Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface::getArguments`.
333+
:method:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolverInterface::getArguments`.
314334
Remember that the controller returned in ``getController`` is a callable.
315335
The purpose of ``getArguments`` is to return the array of arguments that
316336
should be passed to that controller. Exactly how this is done is completely
317-
up to your design, though the built-in :class:`Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver`
337+
up to your design, though the built-in :class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver`
318338
is a good example.
319339

320340
.. image:: /images/components/http_kernel/07-controller-arguments.png
@@ -326,7 +346,7 @@ of arguments that should be passed when executing that callable.
326346
.. sidebar:: Getting the Controller Arguments in the Symfony Framework
327347

328348
Now that you know exactly what the controller callable (usually a method
329-
inside a controller object) is, the ``ControllerResolver`` uses `reflection`_
349+
inside a controller object) is, the ``ArgumentResolver`` uses `reflection`_
330350
on the callable to return an array of the *names* of each of the arguments.
331351
It then iterates over each of these arguments and uses the following tricks
332352
to determine which value should be passed for each argument:
@@ -338,8 +358,19 @@ of arguments that should be passed when executing that callable.
338358
from the ``RouterListener``).
339359

340360
b) If the argument in the controller is type-hinted with Symfony's
341-
:class:`Symfony\\Component\\HttpFoundation\\Request` object, then the
342-
``Request`` is passed in as the value.
361+
:class:`Symfony\\Component\\HttpFoundation\\Request` object, the
362+
``Request`` is passed in as the value. If you have a custom ``Request``
363+
class, it will be injected as long as you extend the Symfony ``Request``.
364+
365+
c) If the function or method argument is `variadic`_ and the ``Request``
366+
``attributes`` bag contains and array for that argument, they will all be
367+
available through the `variadic`_ argument.
368+
369+
This functionality is provided by resolvers implementing the
370+
:class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolverInterface`.
371+
There are four implementations which provide the default behavior of Symfony but
372+
customization is the key here. By implementing the ``ArgumentValueResolverInterface``
373+
yourself and passing this to the ``ArgumentResolver``, you can extend this functionality.
343374

344375
.. _component-http-kernel-calling-controller:
345376

@@ -612,47 +643,52 @@ A full Working Example
612643
----------------------
613644

614645
When using the HttpKernel component, you're free to attach any listeners
615-
to the core events and use any controller resolver that implements the
616-
:class:`Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface`.
617-
However, the HttpKernel component comes with some built-in listeners and
618-
a built-in ControllerResolver that can be used to create a working example::
646+
to the core events, use any controller resolver that implements the
647+
:class:`Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface` and
648+
use any argument resolver that implements the
649+
:class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolverInterface`.
650+
However, the HttpKernel component comes with some built-in listeners, and everything
651+
else that can be used to create a working example::
619652

620-
use Symfony\Component\HttpFoundation\Request;
621-
use Symfony\Component\HttpFoundation\RequestStack;
622-
use Symfony\Component\HttpFoundation\Response;
623-
use Symfony\Component\HttpKernel\HttpKernel;
624-
use Symfony\Component\EventDispatcher\EventDispatcher;
625-
use Symfony\Component\HttpKernel\Controller\ControllerResolver;
626-
use Symfony\Component\HttpKernel\EventListener\RouterListener;
627-
use Symfony\Component\Routing\RouteCollection;
628-
use Symfony\Component\Routing\Route;
629-
use Symfony\Component\Routing\Matcher\UrlMatcher;
630-
use Symfony\Component\Routing\RequestContext;
631-
632-
$routes = new RouteCollection();
633-
$routes->add('hello', new Route('/hello/{name}', array(
634-
'_controller' => function (Request $request) {
635-
return new Response(
636-
sprintf("Hello %s", $request->get('name'))
637-
);
638-
}
639-
)
640-
));
653+
use Symfony\Component\EventDispatcher\EventDispatcher;
654+
use Symfony\Component\HttpFoundation\Request;
655+
use Symfony\Component\HttpFoundation\RequestStack;
656+
use Symfony\Component\HttpFoundation\Response;
657+
use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
658+
use Symfony\Component\HttpKernel\Controller\ControllerResolver;
659+
use Symfony\Component\HttpKernel\EventListener\RouterListener;
660+
use Symfony\Component\HttpKernel\HttpKernel;
661+
use Symfony\Component\Routing\Matcher\UrlMatcher;
662+
use Symfony\Component\Routing\RequestContext;
663+
use Symfony\Component\Routing\Route;
664+
use Symfony\Component\Routing\RouteCollection;
641665

642-
$request = Request::createFromGlobals();
666+
$routes = new RouteCollection();
667+
$routes->add('hello', new Route('/hello/{name}', array(
668+
'_controller' => function (Request $request) {
669+
return new Response(
670+
sprintf("Hello %s", $request->get('name'))
671+
);
672+
})
673+
));
643674

644-
$matcher = new UrlMatcher($routes, new RequestContext());
675+
$request = Request::createFromGlobals();
645676

646-
$dispatcher = new EventDispatcher();
647-
$dispatcher->addSubscriber(new RouterListener($matcher, new RequestStack()));
677+
$matcher = new UrlMatcher($routes, new RequestContext());
648678

649-
$resolver = new ControllerResolver();
650-
$kernel = new HttpKernel($dispatcher, $resolver);
679+
$dispatcher = new EventDispatcher();
680+
$dispatcher->addSubscriber(new RouterListener($matcher, new RequestStack()));
651681

652-
$response = $kernel->handle($request);
653-
$response->send();
682+
$controllerResolver = new ControllerResolver();
683+
$argumentResolver = new ArgumentResolver();
684+
685+
$kernel = new HttpKernel($dispatcher, $controllerResolver, new RequestStack(), $argumentResolver);
686+
687+
$response = $kernel->handle($request);
688+
$response->send();
689+
690+
$kernel->terminate($request, $response);
654691

655-
$kernel->terminate($request, $response);
656692

657693
.. _http-kernel-sub-requests:
658694

@@ -716,3 +752,4 @@ look like this::
716752
.. _`@ParamConverter`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
717753
.. _`@Template`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/view.html
718754
.. _`EmailSenderListener`: https://github.com/symfony/swiftmailer-bundle/blob/master/EventListener/EmailSenderListener.php
755+
.. _variadic: http://php.net/manual/en/functions.arguments.php

create_framework/dependency_injection.rst

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,26 @@ to it::
99
// example.com/src/Simplex/Framework.php
1010
namespace Simplex;
1111

12+
use Symfony\Component\EventDispatcher\EventDispatcher;
1213
use Symfony\Component\Routing;
14+
use Symfony\Component\HttpFoundation;
1315
use Symfony\Component\HttpKernel;
14-
use Symfony\Component\EventDispatcher\EventDispatcher;
1516

1617
class Framework extends HttpKernel\HttpKernel
1718
{
1819
public function __construct($routes)
1920
{
2021
$context = new Routing\RequestContext();
2122
$matcher = new Routing\Matcher\UrlMatcher($routes, $context);
22-
$resolver = new HttpKernel\Controller\ControllerResolver();
23+
24+
$controllerResolver = new HttpKernel\Controller\ControllerResolver();
25+
$argumentResolver = new HttpKernel\Controller\ArgumentResolver();
2326

2427
$dispatcher = new EventDispatcher();
2528
$dispatcher->addSubscriber(new HttpKernel\EventListener\RouterListener($matcher));
2629
$dispatcher->addSubscriber(new HttpKernel\EventListener\ResponseListener('UTF-8'));
2730

28-
parent::__construct($dispatcher, $resolver);
31+
parent::__construct($dispatcher, $controllerResolver, new RequestStack(), $argumentResolver);
2932
}
3033
}
3134

@@ -101,7 +104,8 @@ Create a new file to host the dependency injection container configuration::
101104
->setArguments(array($routes, new Reference('context')))
102105
;
103106
$sc->register('request_stack', 'Symfony\Component\HttpFoundation\RequestStack');
104-
$sc->register('resolver', 'Symfony\Component\HttpKernel\Controller\ControllerResolver');
107+
$sc->register('controller_resolver', 'Symfony\Component\HttpKernel\Controller\ControllerResolver');
108+
$sc->register('argument_resolver', 'Symfony\Component\HttpKernel\Controller\ArgumentResolver');
105109

106110
$sc->register('listener.router', 'Symfony\Component\HttpKernel\EventListener\RouterListener')
107111
->setArguments(array(new Reference('matcher'), new Reference('request_stack')))
@@ -118,7 +122,12 @@ Create a new file to host the dependency injection container configuration::
118122
->addMethodCall('addSubscriber', array(new Reference('listener.exception')))
119123
;
120124
$sc->register('framework', 'Simplex\Framework')
121-
->setArguments(array(new Reference('dispatcher'), new Reference('resolver')))
125+
->setArguments(array(
126+
new Reference('dispatcher'),
127+
new Reference('controller_resolver'),
128+
new Reference('request_stack'),
129+
new Reference('argument_resolver'),
130+
))
122131
;
123132

124133
return $sc;

create_framework/event_dispatcher.rst

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,24 +36,27 @@ the Response instance::
3636
// example.com/src/Simplex/Framework.php
3737
namespace Simplex;
3838

39+
use Symfony\Component\EventDispatcher\EventDispatcher;
3940
use Symfony\Component\HttpFoundation\Request;
4041
use Symfony\Component\HttpFoundation\Response;
41-
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
42-
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
42+
use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface;
4343
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
44-
use Symfony\Component\EventDispatcher\EventDispatcher;
44+
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
45+
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
4546

4647
class Framework
4748
{
48-
private $matcher;
49-
private $resolver;
5049
private $dispatcher;
50+
private $matcher;
51+
private $controllerResolver;
52+
private $argumentResolver;
5153

52-
public function __construct(EventDispatcher $dispatcher, UrlMatcherInterface $matcher, ControllerResolverInterface $resolver)
54+
public function __construct(EventDispatcher $dispatcher, UrlMatcherInterface $matcher, ControllerResolverInterface $controllerResolver, ArgumentResolverInterface $argumentResolver)
5355
{
54-
$this->matcher = $matcher;
55-
$this->resolver = $resolver;
5656
$this->dispatcher = $dispatcher;
57+
$this->matcher = $matcher;
58+
$this->controllerResolver = $controllerResolver;
59+
$this->argumentResolver = $argumentResolver;
5760
}
5861

5962
public function handle(Request $request)
@@ -63,8 +66,8 @@ the Response instance::
6366
try {
6467
$request->attributes->add($this->matcher->match($request->getPathInfo()));
6568

66-
$controller = $this->resolver->getController($request);
67-
$arguments = $this->resolver->getArguments($request, $controller);
69+
$controller = $this->controllerResolver->getController($request);
70+
$arguments = $this->argumentResolver->getArguments($request, $controller);
6871

6972
$response = call_user_func_array($controller, $arguments);
7073
} catch (ResourceNotFoundException $e) {

create_framework/http_kernel_controller_resolver.rst

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ component:
4343
4444
$ composer require symfony/http-kernel
4545
46-
The HttpKernel component has many interesting features, but the one we need
47-
right now is the *controller resolver*. A controller resolver knows how to
48-
determine the controller to execute and the arguments to pass to it, based on
49-
a Request object. All controller resolvers implement the following interface::
46+
The HttpKernel component has many interesting features, but the ones we need
47+
right now are the *controller resolver* and *argument resolver*. A controller resolver knows how to
48+
determine the controller to execute and the argument resolver determines the arguments to pass to it,
49+
based on a Request object. All controller resolvers implement the following interface::
5050

5151
namespace Symfony\Component\HttpKernel\Controller;
5252

@@ -58,6 +58,14 @@ a Request object. All controller resolvers implement the following interface::
5858
function getArguments(Request $request, $controller);
5959
}
6060

61+
.. caution::
62+
63+
The ``getArguments()`` method in the :class:`Symfony\\Component\\Httpkernel\\Controller\\ControllerResolver`
64+
and respective interface :class:`Symfony\\Component\\Httpkernel\\Controller\\ControllerResolverInterface`
65+
are deprecated as of 3.1 and will be removed in 4.0. You can use the
66+
:class:`Symfony\\Component\\Httpkernel\\Controller\\ArgumentResolver` which uses the
67+
:class:`Symfony\\Component\\Httpkernel\\Controller\\ArgumentResolverInterface` instead.
68+
6169
The ``getController()`` method relies on the same convention as the one we
6270
have defined earlier: the ``_controller`` request attribute must contain the
6371
controller associated with the Request. Besides the built-in PHP callbacks,
@@ -74,10 +82,11 @@ resolver from HttpKernel::
7482

7583
use Symfony\Component\HttpKernel;
7684

77-
$resolver = new HttpKernel\Controller\ControllerResolver();
85+
$controllerResolver = new HttpKernel\Controller\ControllerResolver();
86+
$argumentResolver = new HttpKernel\Controller\ArgumentResolver();
7887

79-
$controller = $resolver->getController($request);
80-
$arguments = $resolver->getArguments($request, $controller);
88+
$controller = $controllerResolver->getController($request);
89+
$arguments = $argumentResolver->getArguments($request, $controller);
8190

8291
$response = call_user_func_array($controller, $arguments);
8392

@@ -140,14 +149,12 @@ method is not defined, an argument has no matching attribute, ...).
140149

141150
.. note::
142151

143-
With the great flexibility of the default controller resolver, you might
144-
wonder why someone would want to create another one (why would there be an
145-
interface if not?). Two examples: in Symfony, ``getController()`` is
146-
enhanced to support
147-
:doc:`controllers as services </cookbook/controller/service>`; and in
148-
`FrameworkExtraBundle`_, ``getArguments()`` is enhanced to support
149-
parameter converters, where request attributes are converted to objects
150-
automatically.
152+
With the great flexibility of the default controller resolver and argument
153+
resolver, you might wonder why someone would want to create another one
154+
(why would there be an interface if not?). Two examples: in Symfony,
155+
``getController()`` is enhanced to support :doc:`controllers as services </cookbook/controller/service>`;
156+
and ``getArguments()`` provides an extension point to alter or enhance
157+
the resolving of arguments.
151158

152159
Let's conclude with the new version of our framework::
153160

@@ -174,13 +181,18 @@ Let's conclude with the new version of our framework::
174181
$context = new Routing\RequestContext();
175182
$context->fromRequest($request);
176183
$matcher = new Routing\Matcher\UrlMatcher($routes, $context);
177-
$resolver = new HttpKernel\Controller\ControllerResolver();
184+
185+
$controllerResolver = new HttpKernel\Controller\ControllerResolver();
186+
$argumentResolver = new HttpKernel\Controller\ArgumentResolver();
187+
188+
$controller = $controllerResolver->getController($request);
189+
$arguments = $argumentResolver->getArguments($request, $controller);
178190

179191
try {
180192
$request->attributes->add($matcher->match($request->getPathInfo()));
181193

182-
$controller = $resolver->getController($request);
183-
$arguments = $resolver->getArguments($request, $controller);
194+
$controller = $controllerResolver->getController($request);
195+
$arguments = $argumentResolver->getArguments($request, $controller);
184196

185197
$response = call_user_func_array($controller, $arguments);
186198
} catch (Routing\Exception\ResourceNotFoundException $e) {
@@ -192,7 +204,7 @@ Let's conclude with the new version of our framework::
192204
$response->send();
193205

194206
Think about it once more: our framework is more robust and more flexible than
195-
ever and it still has less than 40 lines of code.
207+
ever and it still has less than 50 lines of code.
196208

197209
.. _`reflection`: http://php.net/reflection
198210
.. _`FrameworkExtraBundle`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy