a SensioLabs Product

The PHP micro-framework
based on the Symfony Components

You are reading the documentation for Silex 2.0. Switch to the documentation for Silex 1.3.

Questions & Feedback

License

Creative Commons License Silex documentation is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.

Making sub-Requests

Since Silex is based on the HttpKernelInterface, it allows you to simulate requests against your application. This means that you can embed a page within another, it also allows you to forward a request which is essentially an internal redirect that does not change the URL.

Basics

You can make a sub-request by calling the handle method on the Application. This method takes three arguments:

  • $request: An instance of the Request class which represents the
    HTTP request.
  • $type: Must be either HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST. Certain listeners are only executed for the master request, so it's important that this is set to SUB_REQUEST.
  • $catch: Catches exceptions and turns them into a response with status code 500. This argument defaults to true. For sub-requests you will most likely want to set it to false.

By calling handle, you can make a sub-request manually. Here's an example:

1
2
3
4
5
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;

$subRequest = Request::create('/');
$response = $app->handle($subRequest, HttpKernelInterface::SUB_REQUEST, false);

There's some more things that you need to keep in mind though. In most cases you will want to forward some parts of the current master request to the sub-request like cookies, server information, or the session.

Here is a more advanced example that forwards said information ($request holds the master request):

1
2
3
4
5
6
7
8
9
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;

$subRequest = Request::create('/', 'GET', array(), $request->cookies->all(), array(), $request->server->all());
if ($request->getSession()) {
    $subRequest->setSession($request->getSession());
}

$response = $app->handle($subRequest, HttpKernelInterface::SUB_REQUEST, false);

To forward this response to the client, you can simply return it from a controller:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
use Silex\Application;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;

$app->get('/foo', function (Application $app, Request $request) {
    $subRequest = Request::create('/', ...);
    $response = $app->handle($subRequest, HttpKernelInterface::SUB_REQUEST, false);

    return $response;
});

If you want to embed the response as part of a larger page you can call Response::getContent:

1
2
3
4
5
$header = ...;
$footer = ...;
$body = $response->getContent();

return $header.$body.$footer;

Rendering pages in Twig templates

The TwigServiceProvider provides a render function that you can use in Twig templates. It gives you a convenient way to embed pages.

1
{{ render('/sidebar') }}

For details, refer to the TwigServiceProvider docs.

Edge Side Includes

You can use ESI either through the HttpCacheServiceProvider or a reverse proxy cache such as Varnish. This also allows you to embed pages, however it also gives you the benefit of caching parts of the page.

Here is an example of how you would embed a page via ESI:

1
<esi:include src="/sidebar" />

For details, refer to the HttpCacheServiceProvider docs.

Dealing with the request base URL

One thing to watch out for is the base URL. If your application is not hosted at the webroot of your web server, then you may have an URL like http://example.org/foo/index.php/articles/42.

In this case, /foo/index.php is your request base path. Silex accounts for this path prefix in the routing process, it reads it from $request->server. In the context of sub-requests this can lead to issues, because if you do not prepend the base path the request could mistake a part of the path you want to match as the base path and cut it off.

You can prevent that from happening by always prepending the base path when constructing a request:

$url = $request->getUriForPath('/');
$subRequest = Request::create($url, 'GET', array(), $request->cookies->all(), array(), $request->server->all());

This is something to be aware of when making sub-requests by hand.

Services depending on the Request

The container is a concept that is global to a Silex application, since the application object is the container. Any request that is run against an application will re-use the same set of services. Since these services are mutable, code in a master request can affect the sub-requests and vice versa. Any services depending on the request service will store the first request that they get (could be master or sub-request), and keep using it, even if that request is already over.

Instead of injecting the request service, you should always inject the request_stack one instead.

Website powered by Symfony and Twig, deployed on
The Silex logo is © 2010-2017 SensioLabs