New in Symfony 2.4: The Request Stack
November 12, 2013 • Published by Fabien Potencier
Warning: This post is about an unsupported Symfony version. Some of this information may be out of date. Read the most recent Symfony Docs.
Dealing with the request in the service container is difficult to say the least. Why is it difficult to inject the request into a service? Because you can have more than one request in a single PHP process (think sub-requests.) So, during the lifetime of the container, the request instance changes.
Let's take this simple service as an example:
1 2 3 4 5 6 7 8 9
use Symfony\Component\HttpFoundation\Request;
class FooService
{
public function __construct(Request $request)
{
$this->request = $request;
}
}
Which request instance the container is going to inject into the foo service when you will ask for it in your application? Well, if you do not use sub-requests, the foo service will consistently get the master request. But if you have some sub-requests, the foo constructor might get one of the sub-request instances depending on when you get the service for the first time. And as soon as the service is created, the request won't change anymore, even when entering or leaving a sub-request. And what if you try to get the service before request handling (like in a CLI command)? You will get an exception.
Before Symfony 2.3, configuring such a service so that it behaves correctly was possible but difficult. In Symfony 2.3, the synchronized services feature makes things much easier. As a matter of fact, some of the most complex features of the container were introduced just to be able to deal correctly with the request service like scopes, strict services, and synchronized services. So many features just to be able to deal with the request service because it is the only one to have a special behavior.
But the real problem is that the request is not a service; the request is a value object... like the response. And as you know, the response instance has never been accessible from the container, and that never caused any problems.
In Symfony 3.0, we will fix the problem once and for all by removing the
request service from the container. But how can a service depend on the request
then? Use the new RequestStack
object.
As of Symfony 2.4, the best practice is to never inject the request
service, but to inject the request_stack
service instead:
1 2 3 4 5 6 7 8 9
use Symfony\Component\HttpFoundation\RequestStack;
class FooService
{
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
}
Then, whenever you need to get the current request instance, call
$requestStack->getCurrentRequest()
. Straightforward.
Note
Outside the handling of a request, $requestStack->getCurrentRequest()
returns null
.
Of course, if the framework gives you a request instance, just use it. In a
controller for instance, don't inject the request stack, use the Request
type-hint on an action method argument instead:
1 2 3 4 5 6 7
class FooController
{
public function indexAction(Request $request)
{
$request->...();
}
}
Tip
The request stack also gives you access to the master request via the
getMasterRequest()
method. Please, use this carefully as it makes your
sub-requests incompatible with ESIs/reverse proxies (where a sub-request is
also a master request).
Help the Symfony project!
As with any Open-Source project, contributing code or documentation is the most common way to help, but we also have a wide range of sponsoring opportunities.
Comments are closed.
To ensure that comments stay relevant, they are closed for old posts.
Just 1 thing I wonder about, you still use Request as typehint, will that typehint contain the getCurrentRequest() etc? It doesn't really make sense to have that on my Request::getCurrentRequest(), I would prefer to use the RequestStack as typehint due to code completion. Or is this not-done?
But the Request class will continue to exist, and it will be what you use to extract the data from the user request. The RequestStack it's just the provider for that value object.
no, that part about the typehint keeps the same and is used the same as before, the only thing that changes is the request stack for services.
Looks like a great solution.
Thanks a lot for this update which will be appreciated by all Sf2 developers I'm sure ;-)
My use case being a multi-tenant application where multiple emails are generated within a single request. Each recipient's email is customised to their install so the UrlGenerator needs to generate links from multiple Request's
Should dived into the code first :)