4968195675_bcde76e1e1_b

HTTP interface: Request -> Response

In the second post of the Symfony2 components series we are going to talk about the HttpKernel component. If the HttpFoundation component provided the building blocks of the HTTP protocol, the HttpKernel component makes use of them to define an abstract process for converting a Request into a Response.

HttpKernelInterface

The HttpKernelInterface is a brilliant idea, both for its simplicity and power. It just says, “Hey, just give me a Request, I’ll take care of it to create a Response“. To be honest, it is not a new idea; projects like Java Servlet or Ruby Rack defined an HTTP interface before. Once again, PHP and/or Symfony2 copy from other languages things that can be useful for the PHP community – which is good!.

To demonstrate its simplicity, the interface has only one method:

public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true);

This simplicity makes it suitable for most web projects, as long as you have a Request object that has to be converted into a Response object. It is so flexible that is used in full-stack frameworks like Symfony2, micro-frameworks like Silex, or in a CMS like Drupal.

All you have to do is implement this interface and generate a Response based on the Request. No matter how you do it, that is what interfaces are for!

Probably you noticed that the handle() method has 2 more input parameters: $type and $catch. The $type parameter defines wheter the given Request is the main request or a sub request. A sub request looks like any other request, but typically is used to render portions of the page. For example, in the Symfony2 full-stack framework, when using the render() function in Twig a sub request is sent. Finally, the $catch parameter configures whether to catch exceptions or not.

Concrete implementation

The component provides a concrete HttpKernelInterface implementation “adapted” to the Symfony2 ecosystem: driven by events -component EventDispatcher- and able to call a controller based on the Request -ControllerResolver-.

Even though you are not required to use this implementation, it is designed to be really flexible: the EventDispatcher and the ControllerResolver are injected using the dependency injection pattern, and dispatch common events that your application can subscribe to:

  • kernel.request
    Dispatched as soon as the request arrives. If any listener return a Response object, all other listeners won’t be called.
  • kernel.controller
    Once the controller is resolved, this event is dispatched and allows changing it.
  • kernel.view
    Dispatched only if the controller does not return a Response. Its goal is to build a Response object from the return value of the Controller - for example, a string -.
  • kernel.response
    Allows to modify or replace the Response object after its creation – for example, adding the Google Analytics tracker code to every page -.
  • kernel.terminate
    Dispatched once the Response has been sent. Can be used to run expensive post-response jobs, such as sending mails or processing data.
  • kernel.exception
    Last chance to convert an Exception into a Response object.

Beyond HttpKernelInterface

stackphpStackPHP is an interesting idea to re-use the web layer based on the HttpKernelInterface. The basic idea is that you wrap your application in decorators so you can add new behavior – HttpKernelInterface middlewares - from the outside.

For example, to decorate a silex app with session and cache middlewares, you’ll have to do something like this:

$stack = (new Stack\Builder())
    ->push('Stack\Session')
    ->push('Symfony\Component\HttpKernel\HttpCache\HttpCache', new Store(__DIR__.'/cache'));

$app = $stack->resolve($app);

A middleware is just an object that implements HttpKernelInterface, take the decorated app as the first constructor argument and decorate the handle() call:

namespace Stack;

use Symfony\Component\HttpKernel\HttpKernelInterface;

class Demo implements HttpKernelInterface
{
    private $app;

    public function __construct(HttpKernelInterface $app, array $options = [])
    {
        $this->app = $app;
    }

    public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
    {
        // ...
        $response = $this->app->handle($request, $type, $catch);
        // ...

        return $response;
    }
}

There are already several middlewares like HttpCache, IpRestrict or Oauth ready to be used.

More info

Photo: “Computer Output”, by David Sjunnesson