Symfony Under The Hood
Symfony Under The Hood
Andreas Hucks
CTO @ SensioLabs Germany
Version 4.0
November 2017
Directories 😍
Directories
├── bin
├── config
├── public
├── src
├── var
└── vendor
Directories
src
├── Controller
└── Kernel.php
No bundles 😱
Bundles are portable.
Your application is not.
Bundles
src
├── AdminBundle
├── ApiBundle
├── CartBundle
└── FrontendBundle
Bundles
src
├── AdminBundle
├── ApiBundle
├── CartBundle
├── FrontendBundle
└── [n-th]Bundle
Flex 😍
No Editions, no installer
Adding Doctrine
composer require orm
github.com/symfony/recipes
Autowiring 😍
Default services.yml
services:
_defaults:
autowire: true
autoconfigure: true
public: false
App:
resource: '../src/*'
exclude: '../src/{Entity,Migrations,Tests,Kernel.php}'
AppController:
resource: '../src/Controller'
tags: ['controller.service_arguments']
# Your own definitions here
Controllers
class GameController extends AbstractController
{
/**
* @Route(path="/", name="app_game")
*/
public function index(Runner $runner)
{
$game = $runner->loadGame();
Symfony Under The Hood
What to expect
Motivation
The Request
$kernel = new Kernel($env, $debug);
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
HttpKernel
$kernel->handle($request);
Request-Response Flow
Request Response
Front

Controller Kernel
Symfony is not an

MVC framework
Symfony is a

Request-Response Framework
Request-Response Flow
Request Response
Front

Controller Kernel
Request-Response Flow
Kernel
Request-Response Flow
Kernel
kernel.request
kernel.controller
kernel.response
kernel.exceptio kernel.view
kernel.terminat
EventDispatcher
$dispatcher = new EventDispatcher();
$dispatcher->addListener('kernel.request', $callable);
EventDispatcher
$dispatcher->dispatch(
'kernel.request',
$responseEvent
);
EventDispatcher
$dispatcher->dispatch(
'kernel.request',
$responseEvent
);
payload
What happened.
Request-Response Flow
Kernel
kernel.request
kernel.controller
kernel.response
kernel.exceptio kernel.view
kernel.terminat
GetResponseEvent
class GetResponseEvent extends KernelEvent
{
public function getResponse()
{
return $this->response;
}
public function setResponse(Response $response)
{
$this->response = $response;
$this->stopPropagation();
}
kernel.request
RouterListener
/**
* @Route(path="/books/{id}", name="book_show")
*/
$params = [
'_controller' => ‘AppControllerBookController::showAction’,
'_route' => 'book_show',
'_route_params' => [
'id' => '37'
],
'id' => ’37'
];
kernel.request
RouterListener
$parameters = $this
->matcher->match($request->getPathInfo());
$request->attributes->add($parameters);
unset(
$parameters[‘_route'],
$parameters[‘_controller']
);
$request->attributes->set(
'_route_params',
$parameters
);
kernel.request
LocaleListener
$request = $event->getRequest();
$request->setDefaultLocale($this->defaultLocale);
if ($locale = $request->attributes->get('_locale')) {
$request->setLocale($locale);
}
kernel.request
Customize!
• Content Negotiation - Parse headers and add attribute
• Verify signed Cookies
• Redirects based on arbitrary criteria
• Custom Locale stuff
• … ?
Request-Response Flow
Kernel
kernel.request
kernel.controller
kernel.response
kernel.exceptio kernel.view
kernel.terminat
FilterControllerEvent
class FilterControllerEvent extends KernelEvent
{
public function getController()
{
return $this->controller;
}
public function setController(callable $controller)
{
$this->controller = $controller;
}
kernel.controller
ControllerListener
class ControllerListener implements EventSubscriberInterface
{
public function __construct(Reader $reader)
{
$this->reader = $reader;
}
public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
// read annotations for class and method
$request = $event->getRequest();
foreach ($configurations as $key => $attributes) {
$request->attributes->set($key, $attributes);
}
kernel.controller
ParamConverterListener
class ParamConverterListener implements EventSubscriberInterface
{
/**
* @var ParamConverterManager
*/
private $manager;
kernel.controller
Request-Response Flow
Kernel
kernel.request
kernel.controller
kernel.response
kernel.exceptio kernel.view
kernel.terminat
Wait… Controllers?
HttpKernel
$response = call_user_func_array(
$controller,
$arguments
);
Request-Response Flow
Kernel
kernel.request
kernel.controller
kernel.response
kernel.exceptio kernel.view
kernel.terminat
GetResponseForControllerResultEvent
class GetResponseForControllerResultEvent extends GetResponseEvent
{
public function getControllerResult()
{
return $this->controllerResult;
}
public function setControllerResult($controllerResult)
{
$this->controllerResult = $controllerResult;
}
kernel.view
TemplateListener
public function onKernelView(
GetResponseForControllerResultEvent $event
) {
/* @var Template $template */
$request = $event->getRequest();
$template = $request->attributes->get(‘_template');
// …
$event->setResponse(
new Response(
$this->twig->render($template->getTemplate()),
$parameters
)
);
kernel.view
Customize!
• Auto serializing and encoding for web service responses
• Together with kernel.controller: 

Action Domain Responder, other patterns?
Request-Response Flow
Kernel
kernel.request
kernel.controller
kernel.response
kernel.exceptio kernel.view
kernel.terminat
GetResponseForExceptionEvent
class GetResponseForExceptionEvent extends GetResponseEvent
{
public function getException()
{
return $this->exception;
}
kernel.exceptio
ExceptionListener
public function onKernelException(
GetResponseForExceptionEvent $event
) {
$exception = $event->getException();
$request = $event->getRequest();
$this->logException($exception
kernel.exceptio
Customize!
• Customize for every exception type you like
• Redirect for some errors
• “were you looking for…” suggestions on 404 pages
• Custom alerts
• … ?
Request-Response Flow
Kernel
kernel.request
kernel.controller
kernel.response
kernel.exceptio kernel.view
kernel.terminat
FilterResponseEvent
class FilterResponseEvent extends KernelEvent
{
public function getResponse()
{
return $this->response;
}
public function setResponse(Response $response)
{
$this->response = $response;
}
kernel.response
WebDebugToolbarListener
$response->headers->set(
'X-Debug-Token-Link',
$this->urlGenerator->generate(
‘_profiler',
array(
'token' => $response->headers->get(‘X-Debug-Token')
),
UrlGeneratorInterface::ABSOLUTE_URL
)
);
$this->injectToolbar($response, $request, $nonces);
kernel.response
SaveSessionListener
$session = $event->getRequest()->getSession();
if ($session && $session->isStarted()) {
$session->save();
$event->getResponse()
->setPrivate()
->setMaxAge(0)
->headers->addCacheControlDirective('must-revalidate');
}
kernel.response
Customize!
• Sign cookies
• Add custom headers
• … ?
Request-Response Flow
Kernel
kernel.request
kernel.controller
kernel.response
kernel.exceptio kernel.view
kernel.terminat
PostResponseEvent
class PostResponseEvent extends KernelEvent
{
public function getResponse()
{
return $this->response;
}
kernel.terminat
EmailSenderListener
public function onTerminate()
{
// send spooled Emails
kernel.terminat
Other Listeners kernel.request
• FirewallListener
• Collector Listeners
Recap
Request Response
Front

Controller Kernel
Thank you :)

Symfony Under the Hood