Scroll down to the script below, click on any sentence (including terminal blocks!) to jump to that spot in the video!
With a Subscription, click any sentence in the script to jump to that part of the video!Login Subscribe
There are several other interesting listeners to
kernel.response. Here's one:
ContextListener... and it's from security! Open that up: Shift+Shift,
Scroll down to find the method we care about:
onKernelResponse(). It says:
Writes the security token into the session.
If you use a "stateful" firewall... which you probably are, unless your security system is a pure, API token-based system, then this is the class that's responsible for taking your authenticated
User object - technically the "token" object that holds it - and saving it into the session. Here it is:
This class is also responsible for unserializing the token at the start of each request - that's in a different method.
Close this class and look back at the event list. Let's see... there's a listener called
DisallowRobotsIndexingListener, which adds an
X-Robots-Tag header set to
noindex if you set a
framework.disallow_search_engine_index option to true. Phew! That option defaults to
true in dev... which is why we see this. So if you... accidentally... deploy your site in dev mode, it won't be indexed.
Let's look at one more:
SessionListener. Open that one up: Shift+Shift then
This class is responsible for actually storing the session information. It extends
AbstractSessionListener... which holds the majority of the logic
This also listens on the
kernel.request event... but we're interested in the
onKernelResponse() method. It does several things... but eventually, it calls
$session->save() to actually put your session data into storage. All these tiny invisible pieces help make your application sing.
Ok, enough playing with these listeners. Close the two session classes and go back to
HttpKernel. After dispatching the
kernel.response event, this calls a
finishRequest() method and then finally returns the
Response that's on the event. Let's see what
finishRequest() does. Ah! It dispatches one more event and then calls
RequestStack object is basically a collection of request objects - something we'll talk more about soon. The
pop() method removes the most-recently-added
Request object from that collection. If you scroll back up to the top of
pop() call does the opposite of
$this->requestStack->push($request). So... we don't know why this request stack thing needs to exist... but we at least know that the current
Request object is added to the
RequestStack at the beginning of handling the request, and then removed at the end.
So... we're done! The
filterResponse() method returns the
handleRaw() returns the same
Response... and then
handle() also returns the
Response... all the way back to
$response = $kernel->handle($request).
We made it! But we haven't sent anything to the user yet: everything is still just stored in PHP memory. The next call takes care of that:
$response->send(). I'll open that up. It's just a fancy way of calling the PHP
header() function to set all the headers and then echo'ing the content. At this point, our response is sent!
index.php, there's one final line:
$kernel->terminate(). Let's find that inside of
HttpKernel. And... wow. I'm personally shocked. This dispatches one final event.
This event is dispatched so late that... if your web server is set up correctly, the response has already been sent to the user! This event isn't used too often... but it is where all the data for the profiler is stored, for example. In fact, that's the only listener to this event:
So that is Symfony's request-response process in depth. A work of art.
It may have seemed like a lot, but if you zoom out, it's delightfully simple: we dispatch an event, find the controller, dispatch an event, find the arguments, dispatch an event, call the controller, dispatch 2 more events in
finishRequest() and then, back in
index.php, we send the headers, echo the content and dispatch one last event. It's... kind of a "find the controller, call the controller" system... with a ton of events mixed in as hook points.
But go back to
HttpKernel and scroll all the way back up to
handle(). Ah yes, this wraps all of our code in a try-catch block. So what happens if an exception is thrown from somewhere in our app? Well, quite a lot. Let's jump into that next.