Skip to content

Commit 25f4985

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

File tree

6 files changed

+250
-99
lines changed

6 files changed

+250
-99
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/dependency_injection.rst

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,35 @@ 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+
$valueResolvers = [
25+
new HttpKernel\Controller\ArgumentValueResolver\ArgumentFromAttributeResolver(),
26+
new HttpKernel\Controller\ArgumentValueResolver\VariadicArgumentValueResolver(),
27+
new HttpKernel\Controller\ArgumentValueResolver\RequestResolver(),
28+
new HttpKernel\Controller\ArgumentValueResolver\DefaultArgumentValueResolver(),
29+
];
30+
31+
$argumentMetadataFactory = new HttpKernel\ControllerMetadata\ArgumentMetadataFactory();
32+
33+
$controllerResolver = new HttpKernel\Controller\ControllerResolver();
34+
$argumentResolver = new HttpKernel\Controller\ArgumentResolver($argumentMetadataFactory, $valueResolvers);
2335

2436
$dispatcher = new EventDispatcher();
2537
$dispatcher->addSubscriber(new HttpKernel\EventListener\RouterListener($matcher));
2638
$dispatcher->addSubscriber(new HttpKernel\EventListener\ResponseListener('UTF-8'));
2739

28-
parent::__construct($dispatcher, $resolver);
40+
parent::__construct($dispatcher, $controllerResolver, new RequestStack(), $argumentResolver);
2941
}
3042
}
3143

@@ -101,7 +113,23 @@ Create a new file to host the dependency injection container configuration::
101113
->setArguments(array($routes, new Reference('context')))
102114
;
103115
$sc->register('request_stack', 'Symfony\Component\HttpFoundation\RequestStack');
104-
$sc->register('resolver', 'Symfony\Component\HttpKernel\Controller\ControllerResolver');
116+
$sc->register('controller_resolver', 'Symfony\Component\HttpKernel\Controller\ControllerResolver');
117+
118+
$sc->register('attribute_value_resolver', 'Symfony\Component\HttpKernel\Controller\ArgumentValueResolver\ArgumentFromAttributeResolver');
119+
$sc->register('request_resolver', 'Symfony\Component\HttpKernel\Controller\ArgumentValueResolver\RequestResolver');
120+
$sc->register('default_argument_resolver', 'Symfony\Component\HttpKernel\Controller\ArgumentValueResolver\DefaultArgumentValueResolver');
121+
$sc->register('variadic_value_resolver', 'Symfony\Component\HttpKernel\Controller\ArgumentValueResolver\VariadicArgumentValueResolver');
122+
123+
$sc->register('argument_metadata_factory', 'Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory');
124+
125+
$sc->register('argument_resolver', 'Symfony\Component\HttpKernel\Controller\ArgumentResolver')
126+
->setArguments(array(new Reference('argument_metadata_factory'), array(
127+
new Reference('attribute_value_resolver'),
128+
new Reference('request_resolver'),
129+
new Reference('default_argument_resolver'),
130+
new Reference('variadic_value_resolver'),
131+
)))
132+
;
105133

106134
$sc->register('listener.router', 'Symfony\Component\HttpKernel\EventListener\RouterListener')
107135
->setArguments(array(new Reference('matcher'), new Reference('request_stack')))
@@ -118,7 +146,12 @@ Create a new file to host the dependency injection container configuration::
118146
->addMethodCall('addSubscriber', array(new Reference('listener.exception')))
119147
;
120148
$sc->register('framework', 'Simplex\Framework')
121-
->setArguments(array(new Reference('dispatcher'), new Reference('resolver')))
149+
->setArguments(array(
150+
new Reference('dispatcher'),
151+
new Reference('controller_resolver'),
152+
new Reference('request_stack'),
153+
new Reference('argument_resolver'),
154+
))
122155
;
123156

124157
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) {

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