Fabien Potencier Benjamin Eberlei
Contributed by Fabien Potencier and Benjamin Eberlei in #8904

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).

Published in #Living on the edge