The HttpKernel Component

The HttpKernel component provides a structured process for convertinga Request into a Response by making use of the EventDispatchercomponent. It's flexible enough to create a full-stack framework (Symfony),a micro-framework (Silex) or an advanced CMS system (Drupal).

Installation

  1. $ composer require symfony/http-kernel

Note

If you install this component outside of a Symfony application, you mustrequire the vendor/autoload.php file in your code to enable the classautoloading mechanism provided by Composer. Readthis article for more details.

The Workflow of a Request

This article explains how to use the HttpKernel features as an independentcomponent in any PHP application. In Symfony applications everything isalready configured and ready to use. Read the Controller andEvents and Event Listeners articles to learn about how to use it to createcontrollers and define events in Symfony applications.

Every HTTP web interaction begins with a request and ends with a response.Your job as a developer is to create PHP code that reads the request information(e.g. the URL) and creates and returns a response (e.g. an HTML page or JSON string).This is a simplified overview of the request workflow in Symfony applications:

  • The user asks for a resource in a browser;
  • The browser sends a request to the server;
  • Symfony gives the application a Request object;
  • The application generates a Response object using the data of the Request object;
  • The server sends back the response to the browser;
  • The browser displays the resource to the user.Typically, some sort of framework or system is built to handle all the repetitivetasks (e.g. routing, security, etc) so that a developer can build each page ofthe application. Exactly how these systems are built varies greatly. The HttpKernelcomponent provides an interface that formalizes the process of starting with arequest and creating the appropriate response. The component is meant to be theheart of any application or framework, no matter how varied the architecture ofthat system:
  1. namespace Symfony\Component\HttpKernel;
  2.  
  3. use Symfony\Component\HttpFoundation\Request;
  4.  
  5. interface HttpKernelInterface
  6. {
  7. // ...
  8.  
  9. /**
  10. * @return Response A Response instance
  11. */
  12. public function handle(
  13. Request $request,
  14. $type = self::MASTER_REQUEST,
  15. $catch = true
  16. );
  17. }

Internally, HttpKernel::handle() -the concrete implementation of HttpKernelInterface::handle() -defines a workflow that starts with a Requestand ends with a Response.

The exact details of this workflow are the key to understanding how the kernel(and the Symfony Framework or any other library that uses the kernel) works.

HttpKernel: Driven by Events

The HttpKernel::handle() method works internally by dispatching events.This makes the method both flexible, but also a bit abstract, since all the"work" of a framework/application built with HttpKernel is actually donein event listeners.

To help explain this process, this document looks at each step of the processand talks about how one specific implementation of the HttpKernel - the SymfonyFramework - works.

Initially, using the HttpKernel doesnot take many steps. You create anevent dispatcher and acontroller and argument resolver(explained below). To complete your working kernel, you'll add more eventlisteners to the events discussed below:

  1. use Symfony\Component\EventDispatcher\EventDispatcher;
  2. use Symfony\Component\HttpFoundation\Request;
  3. use Symfony\Component\HttpFoundation\RequestStack;
  4. use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
  5. use Symfony\Component\HttpKernel\Controller\ControllerResolver;
  6. use Symfony\Component\HttpKernel\HttpKernel;
  7.  
  8. // create the Request object
  9. $request = Request::createFromGlobals();
  10.  
  11. $dispatcher = new EventDispatcher();
  12. // ... add some event listeners
  13.  
  14. // create your controller and argument resolvers
  15. $controllerResolver = new ControllerResolver();
  16. $argumentResolver = new ArgumentResolver();
  17.  
  18. // instantiate the kernel
  19. $kernel = new HttpKernel($dispatcher, $controllerResolver, new RequestStack(), $argumentResolver);
  20.  
  21. // actually execute the kernel, which turns the request into a response
  22. // by dispatching events, calling a controller, and returning the response
  23. $response = $kernel->handle($request);
  24.  
  25. // send the headers and echo the content
  26. $response->send();
  27.  
  28. // trigger the kernel.terminate event
  29. $kernel->terminate($request, $response);

See "A full Working Example" for a more concrete implementation.

For general information on adding listeners to the events below, seeCreating an Event Listener.

Caution

As of 3.1 the HttpKernel accepts afourth argument, which must be an instance ofArgumentResolverInterface.In 4.0 this argument will become mandatory.

There is a wonderful tutorial series on using the HttpKernel component andother Symfony components to create your own framework. SeeIntroduction.

1) The kernel.request Event

Typical Purposes: To add more information to the Request, initializeparts of the system, or return a Response if possible (e.g. a securitylayer that denies access).

Kernel Events Information Table

The first event that is dispatched inside HttpKernel::handleis kernel.request, which may have a variety of different listeners.

Listeners of this event can be quite varied. Some listeners - such as a securitylistener - might have enough information to create a Response object immediately.For example, if a security listener determined that a user doesn't have access,that listener may return a RedirectResponseto the login page or a 403 Access Denied response.

If a Response is returned at this stage, the process skips directly tothe kernel.response event.

Other listeners initialize things or add more information to the request.For example, a listener might determine and set the locale on the Requestobject.

Another common listener is routing. A router listener may process the Requestand determine the controller that should be rendered (see the next section).In fact, the Request object has an "attributes"bag which is a perfect spot to store this extra, application-specific dataabout the request. This means that if your router listener somehow determinesthe controller, it can store it on the Request attributes (which can be usedby your controller resolver).

Overall, the purpose of the kernel.request event is either to create andreturn a Response directly, or to add information to the Request(e.g. setting the locale or setting some other information on the Requestattributes).

Note

When setting a response for the kernel.request event, the propagationis stopped. This means listeners with lower priority won't be executed.

kernel.request in the Symfony Framework

The most important listener to kernel.request in the Symfony Frameworkis the RouterListener.This class executes the routing layer, which returns an array of informationabout the matched request, including the controller and any placeholdersthat are in the route's pattern (e.g. {slug}). See[_Routing component]($521bec8bafa5c76c.md).

This array of information is stored in the Requestobject's attributes array. Adding the routing information here doesn'tdo anything yet, but is used next when resolving the controller.

2) Resolve the Controller

Assuming that no kernel.request listener was able to create a Response,the next step in HttpKernel is to determine and prepare (i.e. resolve) thecontroller. The controller is the part of the end-application's code thatis responsible for creating and returning the Response for a specific page.The only requirement is that it is a PHP callable - i.e. a function, methodon an object or a Closure.

But how you determine the exact controller for a request is entirely upto your application. This is the job of the "controller resolver" - a classthat implements ControllerResolverInterfaceand is one of the constructor arguments to HttpKernel.

Your job is to create a class that implements the interface and fill in itsmethod: getController(). In fact, one default implementation alreadyexists, which you can use directly or learn from:ControllerResolver.This implementation is explained more in the sidebar below:

  1. namespace Symfony\Component\HttpKernel\Controller;
  2.  
  3. use Symfony\Component\HttpFoundation\Request;
  4.  
  5. interface ControllerResolverInterface
  6. {
  7. public function getController(Request $request);
  8. }

Internally, the HttpKernel::handle() method first callsgetController()on the controller resolver. This method is passed the Request and is responsiblefor somehow determining and returning a PHP callable (the controller) basedon the request's information.

Resolving the Controller in the Symfony Framework

The Symfony Framework uses the built-inControllerResolverclass (actually, it uses a sub-class with some extra functionalitymentioned below). This class leverages the information that was placedon the Request object's attributes property during the RouterListener.

getController

The ControllerResolver looks for a _controllerkey on the Request object's attributes property (recall that thisinformation is typically placed on the Request via the RouterListener).This string is then transformed into a PHP callable by doing the following:

  • If the _controller key doesn't follow the recommended PHP namespaceformat (e.g. App\Controller\DefaultController::index) its format istransformed into it. For example, the legacy FooBundle:Default:indexformat would be changed to Acme\FooBundle\Controller\DefaultController::indexAction.This transformation is specific to the ControllerResolversub-class used by the Symfony Framework.
  • A new instance of your controller class is instantiated with noconstructor arguments.
  • If the controller implements ContainerAwareInterface,setContainer() is called on the controller object and the containeris passed to it. This step is also specific to the ControllerResolversub-class used by the Symfony Framework.

3) The kernel.controller Event

Typical Purposes: Initialize things or change the controller just beforethe controller is executed.

Kernel Events Information Table

After the controller callable has been determined, HttpKernel::handle()dispatches the kernel.controller event. Listeners to this event might initializesome part of the system that needs to be initialized after certain thingshave been determined (e.g. the controller, routing information) but beforethe controller is executed. For some examples, see the Symfony section below.

Listeners to this event can also change the controller callable completelyby calling ControllerEvent::setControlleron the event object that's passed to listeners on this event.

kernel.controller in the Symfony Framework

There are a few minor listeners to the kernel.controller event inthe Symfony Framework, and many deal with collecting profiler data whenthe profiler is enabled.

One interesting listener comes from the SensioFrameworkExtraBundle. Thislistener's @ParamConverter functionality allows you to pass a full object(e.g. a Post object) to your controller instead of a scalar value (e.g.an id parameter that was on your route). The listener -ParamConverterListener - uses reflection to look at each of thearguments of the controller and tries to use different methods to convertthose to objects, which are then stored in the attributes property ofthe Request object. Read the next section to see why this is important.

4) Getting the Controller Arguments

Next, HttpKernel::handle() callsArgumentResolverInterface::getArguments().Remember that the controller returned in getController() is a callable.The purpose of getArguments() is to return the array of arguments thatshould be passed to that controller. Exactly how this is done is completelyup to your design, though the built-in ArgumentResolveris a good example.

At this point the kernel has a PHP callable (the controller) and an arrayof arguments that should be passed when executing that callable.

Getting the Controller Arguments in the Symfony Framework

Now that you know exactly what the controller callable (usually a methodinside a controller object) is, the ArgumentResolver uses reflectionon the callable to return an array of the names of each of the arguments.It then iterates over each of these arguments and uses the following tricksto determine which value should be passed for each argument:

  • If the Request attributes bag contains a key that matches the nameof the argument, that value is used. For example, if the first argumentto a controller is $slug and there is a slug key in the Requestattributes bag, that value is used (and typically this value camefrom the RouterListener).
  • If the argument in the controller is type-hinted with Symfony'sRequest object, theRequest is passed in as the value.
  • If the function or method argument is variadic and the Requestattributes bag contains an array for that argument, they will all beavailable through the variadic argument.This functionality is provided by resolvers implementing theArgumentValueResolverInterface.There are four implementations which provide the default behavior ofSymfony but customization is the key here. By implementing theArgumentValueResolverInterface yourself and passing this to theArgumentResolver, you can extend this functionality.

5) Calling the Controller

The next step HttpKernel::handle() does is executing the controller.

The job of the controller is to build the response for the given resource.This could be an HTML page, a JSON string or anything else. Unlike everyother part of the process so far, this step is implemented by the "end-developer",for each page that is built.

Usually, the controller will return a Response object. If this is true,then the work of the kernel is just about done! In this case, the next stepis the kernel.response event.

But if the controller returns anything besides a Response, then the kernelhas a little bit more work to do - kernel.view(since the end goal is always to generate a Response object).

Note

A controller must return something. If a controller returns null,an exception will be thrown immediately.

6) The kernel.view Event

Typical Purposes: Transform a non-Response return value from a controllerinto a Response

Kernel Events Information Table

If the controller doesn't return a Response object, then the kernel dispatchesanother event - kernel.view. The job of a listener to this event is touse the return value of the controller (e.g. an array of data or an object)to create a Response.

This can be useful if you want to use a "view" layer: instead of returninga Response from the controller, you return data that represents the page.A listener to this event could then use this data to create a Response thatis in the correct format (e.g HTML, JSON, etc).

At this stage, if no listener sets a response on the event, then an exceptionis thrown: either the controller or one of the view listeners must alwaysreturn a Response.

Note

When setting a response for the kernel.view event, the propagationis stopped. This means listeners with lower priority won't be executed.

kernel.view in the Symfony Framework

There is no default listener inside the Symfony Framework for the kernel.viewevent. However, SensioFrameworkExtraBundledoes add a listener to thisevent. If your controller returns an array, and you place the @Templateannotation above the controller, then this listener renders a template,passes the array you returned from your controller to that template, andcreates a Response containing the returned content from that template.

Additionally, a popular community bundle FOSRestBundle implementsa listener on this event which aims to give you a robust view layercapable of using a single controller to return many different content-typeresponses (e.g. HTML, JSON, XML, etc).

7) The kernel.response Event

Typical Purposes: Modify the Response object just before it is sent

Kernel Events Information Table

The end goal of the kernel is to transform a Request into a Response. TheResponse might be created during the kernel.requestevent, returned from the controller,or returned by one of the listeners to the kernel.viewevent.

Regardless of who creates the Response, another event - kernel.responseis dispatched directly afterwards. A typical listener to this event will modifythe Response object in some way, such as modifying headers, adding cookies,or even changing the content of the Response itself (e.g. injecting someJavaScript before the end </body> tag of an HTML response).

After this event is dispatched, the final Response object is returnedfrom handle(). In themost typical use-case, you can then call the send()method, which sends the headers and prints the Response content.

kernel.response in the Symfony Framework

There are several minor listeners on this event inside the Symfony Framework,and most modify the response in some way. For example, theWebDebugToolbarListenerinjects some JavaScript at the bottom of your page in the dev environmentwhich causes the web debug toolbar to be displayed. Another listener,ContextListenerserializes the current user's information into thesession so that it can be reloaded on the next request.

8) The kernel.terminate Event

Typical Purposes: To perform some "heavy" action after the response hasbeen streamed to the user

Kernel Events Information Table

The final event of the HttpKernel process is kernel.terminate and is uniquebecause it occurs after the HttpKernel::handle() method, and after theresponse is sent to the user. Recall from above, then the code that usesthe kernel, ends like this:

  1. // sends the headers and echoes the content
  2. $response->send();
  3.  
  4. // triggers the kernel.terminate event
  5. $kernel->terminate($request, $response);

As you can see, by calling $kernel->terminate after sending the response,you will trigger the kernel.terminate event where you can perform certainactions that you may have delayed in order to return the response as quicklyas possible to the client (e.g. sending emails).

Caution

Internally, the HttpKernel makes use of the fastcgi_finish_requestPHP function. This means that at the moment, only the PHP FPM serverAPI is able to send a response to the client while the server's PHP processstill performs some tasks. With all other server APIs, listeners to kernel.terminateare still executed, but the response is not sent to the client until theyare all completed.

Note

Using the kernel.terminate event is optional, and should only becalled if your kernel implements TerminableInterface.

kernel.terminate in the Symfony Framework

If you use the memory spooling option of thedefault Symfony mailer, then the EmailSenderListener is activated, whichactually delivers any emails that you scheduled to send during the request.

Handling Exceptions: the kernel.exception Event

Typical Purposes: Handle some type of exception and create an appropriateResponse to return for the exception

Kernel Events Information Table

If an exception is thrown at any point inside HttpKernel::handle(), anotherevent - kernel.exception is thrown. Internally, the body of the handle()function is wrapped in a try-catch block. When any exception is thrown, thekernel.exception event is dispatched so that your system can somehow respondto the exception.

Each listener to this event is passed a ExceptionEventobject, which you can use to access the original exception via thegetException()method. A typical listener on this event will check for a certain type ofexception and create an appropriate error Response.

For example, to generate a 404 page, you might throw a special type of exceptionand then add a listener on this event that looks for this exception andcreates and returns a 404 Response. In fact, the HttpKernel componentcomes with an ExceptionListener,which if you choose to use, will do this and more by default (see the sidebarbelow for more details).

Note

When setting a response for the kernel.exception event, the propagationis stopped. This means listeners with lower priority won't be executed.

kernel.exception in the Symfony Framework

There are two main listeners to kernel.exception when using theSymfony Framework.

ExceptionListener in the HttpKernel Component

The first comes core to the HttpKernel componentand is called ExceptionListener.The listener has several goals:

  • The thrown exception is converted into aFlattenExceptionobject, which contains all the information about the request, but whichcan be printed and serialized.
  • If the original exception implementsHttpExceptionInterface,then getStatusCode() and getHeaders() are called on the exceptionand used to populate the headers and status code of the FlattenExceptionobject. The idea is that these are used in the next step when creatingthe final response. If you want to set custom HTTP headers, you can alwaysuse the setHeaders() method on exceptions derived from theHttpException class.
  • If the original exception implementsRequestExceptionInterface,then the status code of the FlattenException object is populated with400 and no other headers are modified.
  • A controller is executed and passed the flattened exception. The exactcontroller to render is passed as a constructor argument to this listener.This controller will return the final Response for this error page.ExceptionListener in the Security Component

The other important listener is theExceptionListener.The goal of this listener is to handle security exceptions and, whenappropriate, help the user to authenticate (e.g. redirect to the loginpage).

Creating an Event Listener

As you've seen, you can create and attach event listeners to any of the eventsdispatched during the HttpKernel::handle() cycle. Typically a listener is a PHPclass with a method that's executed, but it can be anything. For more informationon creating and attaching event listeners, see The EventDispatcher Component.

The name of each of the "kernel" events is defined as a constant on theKernelEvents class. Additionally, eachevent listener is passed a single argument, which is some sub-class of KernelEvent.This object contains information about the current state of the system andeach event has their own event object:

NameKernelEvents ConstantArgument passed to the listener
kernel.requestKernelEvents::REQUESTRequestEvent
kernel.controllerKernelEvents::CONTROLLERControllerEvent
kernel.controller_argumentsKernelEvents::CONTROLLER_ARGUMENTSControllerArgumentsEvent
kernel.viewKernelEvents::VIEWViewEvent
kernel.responseKernelEvents::RESPONSEResponseEvent
kernel.finish_requestKernelEvents::FINISH_REQUESTFinishRequestEvent
kernel.terminateKernelEvents::TERMINATETerminateEvent
kernel.exceptionKernelEvents::EXCEPTIONExceptionEvent

Deprecated since version 4.3: Since Symfony 4.3, most of the event classes were renamed.The following old classes were deprecated:

A full Working Example

When using the HttpKernel component, you're free to attach any listenersto the core events, use any controller resolver that implements theControllerResolverInterface anduse any argument resolver that implements theArgumentResolverInterface.However, the HttpKernel component comes with some built-in listeners and everythingelse that can be used to create a working example:

  1. use Symfony\Component\EventDispatcher\EventDispatcher;
  2. use Symfony\Component\HttpFoundation\Request;
  3. use Symfony\Component\HttpFoundation\RequestStack;
  4. use Symfony\Component\HttpFoundation\Response;
  5. use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
  6. use Symfony\Component\HttpKernel\Controller\ControllerResolver;
  7. use Symfony\Component\HttpKernel\EventListener\RouterListener;
  8. use Symfony\Component\HttpKernel\HttpKernel;
  9. use Symfony\Component\Routing\Matcher\UrlMatcher;
  10. use Symfony\Component\Routing\RequestContext;
  11. use Symfony\Component\Routing\Route;
  12. use Symfony\Component\Routing\RouteCollection;
  13.  
  14. $routes = new RouteCollection();
  15. $routes->add('hello', new Route('/hello/{name}', [
  16. '_controller' => function (Request $request) {
  17. return new Response(
  18. sprintf("Hello %s", $request->get('name'))
  19. );
  20. }]
  21. ));
  22.  
  23. $request = Request::createFromGlobals();
  24.  
  25. $matcher = new UrlMatcher($routes, new RequestContext());
  26.  
  27. $dispatcher = new EventDispatcher();
  28. $dispatcher->addSubscriber(new RouterListener($matcher, new RequestStack()));
  29.  
  30. $controllerResolver = new ControllerResolver();
  31. $argumentResolver = new ArgumentResolver();
  32.  
  33. $kernel = new HttpKernel($dispatcher, $controllerResolver, new RequestStack(), $argumentResolver);
  34.  
  35. $response = $kernel->handle($request);
  36. $response->send();
  37.  
  38. $kernel->terminate($request, $response);

Sub Requests

In addition to the "main" request that's sent into HttpKernel::handle(),you can also send so-called "sub request". A sub request looks and acts likeany other request, but typically serves to render just one small portion ofa page instead of a full page. You'll most commonly make sub-requests fromyour controller (or perhaps from inside a template, that's being rendered byyour controller).

To execute a sub request, use HttpKernel::handle(), but change the secondargument as follows:

  1. use Symfony\Component\HttpFoundation\Request;
  2. use Symfony\Component\HttpKernel\HttpKernelInterface;
  3.  
  4. // ...
  5.  
  6. // create some other request manually as needed
  7. $request = new Request();
  8. // for example, possibly set its _controller manually
  9. $request->attributes->set('_controller', '...');
  10.  
  11. $response = $kernel->handle($request, HttpKernelInterface::SUB_REQUEST);
  12. // do something with this response

This creates another full request-response cycle where this new Request istransformed into a Response. The only difference internally is that somelisteners (e.g. security) may only act upon the master request. Each listeneris passed some sub-class of KernelEvent,whose isMasterRequest()can be used to check if the current request is a "master" or "sub" request.

For example, a listener that only needs to act on the master request maylook like this:

  1. use Symfony\Component\HttpKernel\Event\RequestEvent;
  2. // ...
  3.  
  4. public function onKernelRequest(RequestEvent $event)
  5. {
  6. if (!$event->isMasterRequest()) {
  7. return;
  8. }
  9.  
  10. // ...
  11. }

Locating Resources

The HttpKernel component is responsible of the bundle mechanism used in Symfonyapplications. The key feature of the bundles is that they allow to override anyresource used by the application (config files, templates, controllers,translation files, etc.)

This overriding mechanism works because resources are referenced not by theirphysical path but by their logical path. For example, the services.xml filestored in the Resources/config/ directory of a bundle called FooBundle isreferenced as @FooBundle/Resources/config/services.xml. This logical pathwill work when the application overrides that file and even if you change thedirectory of FooBundle.

The HttpKernel component provides a method called locateResource()which can be used to transform logical paths into physical paths:

  1. $path = $kernel->locateResource('@FooBundle/Resources/config/services.xml');

Learn more