8000 Documented the ArgumentResolver along the ControllerResolver · symfony/symfony-docs@947626c · GitHub
[go: up one dir, main page]

Skip to content

Commit 947626c

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

File tree

3 files changed

+184
-80
lines changed

3 files changed

+184
-80
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)
0