Skip to content

Commit bd2fb26

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

File tree

2 files changed

+160
-69
lines changed

2 files changed

+160
-69
lines changed

components/http_kernel/introduction.rst

Lines changed: 108 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,22 @@ is really simple and involves creating an
8585
:doc:`event dispatcher </components/event_dispatcher/introduction>` and a
8686
:ref:`controller resolver <component-http-kernel-resolve-controller>` (explained
8787
below). To complete your working kernel, you'll add more event listeners
88-
to the events discussed below::
88+
to the events discussed below
89+
90+
.. caution::
91+
92+
As of 3.1 the :class:`Symfony\\Component\\Httpkernel\\HttpKernel` accepts a fourth argument, which
93+
should be an instance of :class:`Symfony\\Component\\Httpkernel\\Controller\\ArgumentResolverInterface`.
94+
In 4.0 this argument will become mandatory and the :class:`Symfony\\Component\\Httpkernel\\HttpKernel`
95+
will no longer be able to fall back to the :class:`Symfony\\Component\\Httpkernel\\Controller\\ControllerResolver`.
96+
97+
.. code-block:: php
8998
9099
use Symfony\Component\HttpFoundation\Request;
91100
use Symfony\Component\HttpKernel\HttpKernel;
92101
use Symfony\Component\EventDispatcher\EventDispatcher;
102+
use Symfony\Component\HttpFoundation\RequestStack;
103+
use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
93104
use Symfony\Component\HttpKernel\Controller\ControllerResolver;
94105
95106
// create the Request object
@@ -98,10 +109,16 @@ to the events discussed below::
98109
$dispatcher = new EventDispatcher();
99110
// ... add some event listeners
100111
101-
// create your controller resolver
102-
$resolver = new ControllerResolver();
112+
$valueResolvers = [
113+
// ... add some implementations of ArgumentValueResolverInterface
114+
];
115+
116+
// create your controller and argument resolver
117+
$controllerResolver = new ControllerResolver();
118+
$argumentResolver = new ArgumentResolver($argumentMetadataFactory, $valueResolvers);
119+
103120
// instantiate the kernel
104-
$kernel = new HttpKernel($dispatcher, $resolver);
121+
$kernel = new HttpKernel($dispatcher, $controllerResolver, new RequestStack(), $argumentResolver);
105122
106123
// actually execute the kernel, which turns the request into a response
107124
// by dispatching events, calling a controller, and returning the response
@@ -212,7 +229,19 @@ Your job is to create a class that implements the interface and fill in its
212229
two methods: ``getController`` and ``getArguments``. In fact, one default
213230
implementation already exists, which you can use directly or learn from:
214231
:class:`Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver`.
215-
This implementation is explained more in the sidebar below::
232+
This implementation is explained more in the sidebar below
233+
234+
235+
.. caution::
236+
237+
The `getArguments()` method in the :class:`Symfony\\Component\\Httpkernel\\Controller\\ControllerResolver`
238+
and respective interface :class:`Symfony\\Component\\Httpkernel\\Controller\\ControllerResolverInterface`
239+
are deprecated as of 3.1 and will be removed in 4.0. You can use the
240+
:class:`Symfony\\Component\\Httpkernel\\Controller\\ArgumentResolver` which uses the
241+
:class:`Symfony\\Component\\Httpkernel\\Controller\\ArgumentResolverInterface` instead.
242+
243+
244+
.. code-block:: php
216245
217246
namespace Symfony\Component\HttpKernel\Controller;
218247
@@ -231,7 +260,7 @@ on the controller resolver. This method is passed the ``Request`` and is respons
231260
for somehow determining and returning a PHP callable (the controller) based
232261
on the request's information.
233262

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

237266
.. sidebar:: Resolving the Controller in the Symfony Framework
@@ -310,11 +339,11 @@ on the event object that's passed to listeners on this event.
310339
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
311340

312341
Next, ``HttpKernel::handle`` calls
313-
:method:`Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface::getArguments`.
342+
:method:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolverInterface::getArguments`.
314343
Remember that the controller returned in ``getController`` is a callable.
315344
The purpose of ``getArguments`` is to return the array of arguments that
316345
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`
346+
up to your design, though the built-in :class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver`
318347
is a good example.
319348

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

328357
Now that you know exactly what the controller callable (usually a method
329-
inside a controller object) is, the ``ControllerResolver`` uses `reflection`_
358+
inside a controller object) is, the ``ArgumentResolver`` uses `reflection`_
330359
on the callable to return an array of the *names* of each of the arguments.
331360
It then iterates over each of these arguments and uses the following tricks
332361
to determine which value should be passed for each argument:
@@ -339,7 +368,18 @@ of arguments that should be passed when executing that callable.
339368

340369
b) If the argument in the controller is type-hinted with Symfony's
341370
:class:`Symfony\\Component\\HttpFoundation\\Request` object, then the
342-
``Request`` is passed in as the value.
371+
``Request`` is passed in as the value. If you have a custom class extending
372+
the ``Request``, this is also accepted.
373+
374+
c) If the function or method argument is `variadic`_ and the ``Request``
375+
``attributes`` bag contains and array for that argument, they will all be
376+
available through the `variadic`_ argument.
377+
378+
This functionality is provided by resolvers implementing the
379+
:class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolverInterface`.
380+
There are four implementations which provide the default behavior of Symfony but
381+
customization is the key here. By implementing the ``ArgumentValueResolverInterface``
382+
yourself and passing this to the ``ArgumentResolver``, you can extend this functionality.
343383

344384
.. _component-http-kernel-calling-controller:
345385

@@ -612,47 +652,64 @@ A full Working Example
612652
----------------------
613653

614654
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::
655+
to the core events, use any controller resolver that implements the
656+
:class:`Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface` and
657+
use any argument resolver that implements the
658+
:class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolverInterface`.
659+
However, the HttpKernel component comes with some built-in listeners, and everything
660+
else that can be used to create a working example::
661+
662+
use Symfony\Component\EventDispatcher\EventDispatcher;
663+
use Symfony\Component\HttpFoundation\Request;
664+
use Symfony\Component\HttpFoundation\RequestStack;
665+
use Symfony\Component\HttpFoundation\Response;
666+
use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
667+
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolver\ArgumentFromAttributeResolver;
668+
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolver\DefaultArgumentValueResolver;
669+
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolver\RequestResolver;
670+
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolver\VariadicArgumentValueResolver;
671+
use Symfony\Component\HttpKernel\Controller\ControllerResolver;
672+
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory;
673+
use Symfony\Component\HttpKernel\EventListener\RouterListener;
674+
use Symfony\Component\HttpKernel\HttpKernel;
675+
use Symfony\Component\Routing\Matcher\UrlMatcher;
676+
use Symfony\Component\Routing\RequestContext;
677+
use Symfony\Component\Routing\Route;
678+
use Symfony\Component\Routing\RouteCollection;
679+
680+
$routes = new RouteCollection();
681+
$routes->add('hello', new Route('/hello/{name}', array(
682+
'_controller' => function (Request $request) {
683+
return new Response(
684+
sprintf("Hello %s", $request->get('name'))
685+
);
686+
})
687+
));
688+
689+
$request = Request::createFromGlobals();
690+
691+
$matcher = new UrlMatcher($routes, new RequestContext());
692+
693+
$dispatcher = new EventDispatcher();
694+
$dispatcher->addSubscriber(new RouterListener($matcher, new RequestStack()));
695+
696+
$argumentValueResolvers = array(
697+
new ArgumentFromAttributeResolver(),
698+
new VariadicArgumentValueResolver(),
699+
new RequestResolver(),
700+
new DefaultArgumentValueResolver(),
701+
);
702+
703+
$controllerResolver = new ControllerResolver();
704+
$argumentResolver = new ArgumentResolver(new ArgumentMetadataFactory(), $argumentValueResolvers);
705+
706+
$kernel = new HttpKernel($dispatcher, $controllerResolver, new RequestStack(), $argumentResolver);
707+
708+
$response = $kernel->handle($request);
709+
$response->send();
710+
711+
$kernel->terminate($request, $response);
619712

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-
));
641-
642-
$request = Request::createFromGlobals();
643-
644-
$matcher = new UrlMatcher($routes, new RequestContext());
645-
646-
$dispatcher = new EventDispatcher();
647-
$dispatcher->addSubscriber(new RouterListener($matcher, new RequestStack()));
648-
649-
$resolver = new ControllerResolver();
650-
$kernel = new HttpKernel($dispatcher, $resolver);
651-
652-
$response = $kernel->handle($request);
653-
$response->send();
654-
655-
$kernel->terminate($request, $response);
656713

657714
.. _http-kernel-sub-requests:
658715

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

create_framework/http_kernel_controller_resolver.rst

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,20 @@ 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
50+
51+
.. caution::
52+
53+
The `getArguments()` method in the :class:`Symfony\\Component\\Httpkernel\\Controller\\ControllerResolver`
54+
and respective interface :class:`Symfony\\Component\\Httpkernel\\Controller\\ControllerResolverInterface`
55+
are deprecated as of 3.1 and will be removed in 4.0. You can use the
56+
:class:`Symfony\\Component\\Httpkernel\\Controller\\ArgumentResolver` which uses the
57+
:class:`Symfony\\Component\\Httpkernel\\Controller\\ArgumentResolverInterface` instead.
58+
59+
.. code-block:: php
5060
5161
namespace Symfony\Component\HttpKernel\Controller;
5262
@@ -58,6 +68,12 @@ a Request object. All controller resolvers implement the following interface::
5868
function getArguments(Request $request, $controller);
5969
}
6070
71+
// ...
72+
interface ArgumentResolverInterface
73+
{
74+
function getArguments(Request $request, $controller);
75+
}
76+
6177
The ``getController()`` method relies on the same convention as the one we
6278
have defined earlier: the ``_controller`` request attribute must contain the
6379
controller associated with the Request. Besides the built-in PHP callbacks,
@@ -74,10 +90,14 @@ resolver from HttpKernel::
7490

7591
use Symfony\Component\HttpKernel;
7692

77-
$resolver = new HttpKernel\Controller\ControllerResolver();
93+
$valueResolvers = [/* array of ArgumentValueResolverInterface implemetations */];
94+
$argumentMetadataFactory = new Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory();
95+
96+
$controllerResolver = new HttpKernel\Controller\ControllerResolver();
97+
$argumentResolver = new HttpKernel\Controller\ArgumentResolver($argumentMetadataFactory, $valueResolvers);
7898

79-
$controller = $resolver->getController($request);
80-
$arguments = $resolver->getArguments($request, $controller);
99+
$controller = $controllerResolver->getController($request);
100+
$arguments = $argumentResolver->getArguments($request, $controller);
81101

82102
$response = call_user_func_array($controller, $arguments);
83103

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

141161
.. note::
142162

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.
163+
With the great flexibility of the default controller resolver and argument
164+
resolver, you might wonder why someone would want to create another one
165+
(why would there be an interface if not?). Two examples: in Symfony,
166+
``getController()`` is enhanced to support :doc:`controllers as services </cookbook/controller/service>`;
167+
and ``getArguments()`` provides an extension point to alter or enhance
168+
the resolving of arguments.
151169

152170
Let's conclude with the new version of our framework::
153171

@@ -156,6 +174,7 @@ Let's conclude with the new version of our framework::
156174

157175
use Symfony\Component\HttpFoundation\Request;
158176
use Symfony\Component\HttpFoundation\Response;
177+
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolver as ArgumentValueResolver;
159178
use Symfony\Component\Routing;
160179
use Symfony\Component\HttpKernel;
161180

@@ -174,13 +193,27 @@ Let's conclude with the new version of our framework::
174193
$context = new Routing\RequestContext();
175194
$context->fromRequest($request);
176195
$matcher = new Routing\Matcher\UrlMatcher($routes, $context);
177-
$resolver = new HttpKernel\Controller\ControllerResolver();
196+
197+
$valueResolvers = [
198+
new ArgumentValueResolver\ArgumentFromAttributeResolver(),
199+
new ArgumentValueResolver\VariadicArgumentValueResolver(),
200+
new ArgumentValueResolver\RequestResolver(),
201+
new ArgumentValueResolver\DefaultArgumentValueResolver(),
202+
];
203+
204+
$argumentMetadataFactory = new Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory();
205+
206+
$controllerResolver = new HttpKernel\Controller\ControllerResolver();
207+
$argumentResolver = new HttpKernel\Controller\ArgumentResolver($argumentMetadataFactory, $valueResolvers);
208+
209+
$controller = $controllerResolver->getController($request);
210+
$arguments = $argumentResolver->getArguments($request, $controller);
178211

179212
try {
180213
$request->attributes->add($matcher->match($request->getPathInfo()));
181214

182-
$controller = $resolver->getController($request);
183-
$arguments = $resolver->getArguments($request, $controller);
215+
$controller = $controllerResolver->getController($request);
216+
$arguments = $argumentResolver->getArguments($request, $controller);
184217

185218
$response = call_user_func_array($controller, $arguments);
186219
} catch (Routing\Exception\ResourceNotFoundException $e) {
@@ -192,7 +225,7 @@ Let's conclude with the new version of our framework::
192225
$response->send();
193226

194227
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.
228+
ever and it still has less than 60 lines of code.
196229

197230
.. _`reflection`: http://php.net/reflection
198231
.. _`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