Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Symfony Internals
Search
Andreas Hucks
January 27, 2018
Programming
3
740
Symfony Internals
PHPBenelux 2018
Andreas Hucks
January 27, 2018
Tweet
Share
More Decks by Andreas Hucks
See All by Andreas Hucks
Divide and Conquer (LonghornPHP 2019)
meandmymonkey
0
110
Divide and Conquer
meandmymonkey
1
610
Deptrac - Keep Your Architecture Clean
meandmymonkey
0
590
Introduction to Docker at PHPBenelux2015
meandmymonkey
3
790
Best Practices in Symfony2
meandmymonkey
0
320
Introduction to Docker at PHPNW2014
meandmymonkey
4
390
O(ops), Authentication!
meandmymonkey
4
780
Best Practices in Symfony2
meandmymonkey
15
1.7k
Best Practices in Symfony2
meandmymonkey
28
4.5k
Other Decks in Programming
See All in Programming
SymfonyCon Vienna 2025: Twig, still relevant in 2025?
fabpot
3
1.2k
フロントエンドのディレクトリ構成どうしてる? Feature-Sliced Design 導入体験談
osakatechlab
8
4.1k
Criando Commits Incríveis no Git
marcelgsantos
2
170
たのしいparse.y
ydah
3
120
HTTP compression in PHP and Symfony apps
dunglas
2
1.7k
テストケースの名前はどうつけるべきか?
orgachem
PRO
0
130
DevFest Tokyo 2025 - Flutter のアプリアーキテクチャ現在地点
wasabeef
5
900
Асинхронность неизбежна: как мы проектировали сервис уведомлений
lamodatech
0
700
103 Early Hints
sugi_0000
1
220
testcontainers のススメ
sgash708
1
120
モバイルアプリにおける自動テストの導入戦略
ostk0069
0
110
テスト自動化失敗から再挑戦しチームにオーナーシップを委譲した話/STAC2024 macho
ma_cho29
1
1.3k
Featured
See All Featured
Agile that works and the tools we love
rasmusluckow
328
21k
What's in a price? How to price your products and services
michaelherold
243
12k
Imperfection Machines: The Place of Print at Facebook
scottboms
266
13k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
32
2.7k
The Language of Interfaces
destraynor
154
24k
Scaling GitHub
holman
458
140k
StorybookのUI Testing Handbookを読んだ
zakiyama
27
5.3k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
5
440
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
159
15k
Automating Front-end Workflow
addyosmani
1366
200k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
26
1.5k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
330
21k
Transcript
Symfony Internals
Symfony Internals Andreas Hucks CTO @ SensioLabs Germany
2008
Conference Hotel
Auditorium
Lunch
Socials
VIP Lounge
Wellness
Survey
Motivation
What to expect
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
None
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.exception kernel.view kernel.terminate
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.exception kernel.view kernel.terminate
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'
=> ‘App\Controller\BookController::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.exception kernel.view kernel.terminate
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.exception kernel.view kernel.terminate
Wait… Controllers?
HttpKernel $response = \call_user_func_array( $controller, $arguments );
Request-Response Flow Kernel kernel.request kernel.controller kernel.response kernel.exception kernel.view kernel.terminate
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.exception kernel.view kernel.terminate
GetResponseForExceptionEvent class GetResponseForExceptionEvent extends GetResponseEvent { public function getException() {
return $this->exception; } kernel.exception
ExceptionListener public function onKernelException( GetResponseForExceptionEvent $event ) { $exception =
$event->getException(); $request = $event->getRequest(); $this->logException($exception kernel.exception
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.exception kernel.view kernel.terminate
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.exception kernel.view kernel.terminate
PostResponseEvent class PostResponseEvent extends KernelEvent { public function getResponse() {
return $this->response; } kernel.terminate
EmailSenderListener public function onTerminate() { // send spooled Emails kernel.terminate
Other Listeners kernel.request • FirewallListener • Collector Listeners
Recap Request Response Front Controller Kernel
Container & Cache
Container & Cache protected function getRouterService() { $this->services[‘router'] = $instance
= new \Symfony\Bundle\…\Router( $this, ‘/app/config/routing_dev.yml' ) // [snip] return $instance; }
Container & Cache Cache class for this request? Does it
exists? require() it yes Build Container no
Container & Cache Cache class for this request? Does it
exists? require() it yes Build Container no
Container Build Register Bundles Register Extensions & Compiler Passes Register
Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers (quite a few)
Container Build Register Bundles Register Extensions & Compiler Passes Register
Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers
Registering Bundles public function registerBundles() { $bundles = [ new
Symfony\Bundle\FrameworkBundle\FrameworkBundle(), new Symfony\Bundle\SecurityBundle\SecurityBundle(), new Symfony\Bundle\TwigBundle\TwigBundle(), // [snip] ]; return $bundles; }
Container Build Register Bundles Register Extensions & Compiler Passes Register
Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers
Registering Extensions protected function prepareContainer(ContainerBuilder $container) { $extensions = array();
foreach ($this->bundles as $bundle) { if ($extension = $bundle->getContainerExtension()) { $container->registerExtension($extension); $extensions[] = $extension->getAlias(); } // [snip] }
Container Build Register Bundles Register Extensions & Compiler Passes Register
Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers
Register Project-Level Container Config public function registerContainerConfiguration( LoaderInterface $loader )
{ $loader->load( $this->getRootDir(). ’/config/config_’.$this->getEnvironment().'.yml' ); }
Container Build Register Bundles Register Extensions & Compiler Passes Register
Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers
Extensions class TwigExtension extends Extension { public function load(array $configs,
ContainerBuilder $container) { $loader = new XmlFileLoader( $container, new FileLocator(__DIR__.’/../Resources/config') ); $loader->load('twig.xml');
Customize! • Load config files • Do not load config
files depending on parameter • Set Parameters, manipulate parameters • BETTER NOT manipulate services (they are not complete at this point, see next slides)
Container Build Register Bundles Register Extensions & Compiler Passes Register
Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers
Compiler Passes public function process(ContainerBuilder $container) { if (!$container->has('app.myservice')) {
return; } $definition = $container->findDefinition( 'app.myservice' ); // find formatter service $definition->addMethodCall( 'addFormatter', array(new Reference($formatterId)) ); }
(A few) Compiler Passes • ServiceLocatorTagPass • DecoratorServicePass • ResolveParameterPlaceHoldersPass
• ResolveFactoryClassPass • ResolveNamedArgumentsPass • AutowirePass • RemovePrivateAliasesPass • ReplaceAliasByActualDefinitionPass • RemoveAbstractDefinitionsPass • InlineServiceDefinitionsPass • RemoveUnusedDefinitionsPass
(A few) Compiler Passes • ServiceLocatorTagPass • DecoratorServicePass • ResolveParameterPlaceHoldersPass
• ResolveFactoryClassPass • ResolveNamedArgumentsPass • AutowirePass • RemovePrivateAliasesPass • ReplaceAliasByActualDefinitionPass • RemoveAbstractDefinitionsPass • InlineServiceDefinitionsPass • RemoveUnusedDefinitionsPass
Customize! • Add method calls to services based on tags
• Manipulate services that depend on the full service config
Container Build Register Bundles Register Extensions & Compiler Passes Register
Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers
Dump Container protected function dumpContainer( ConfigCache $cache, ContainerBuilder $container, $class,
$baseClass ) { $dumper = new PhpDumper($container); $content = $dumper->dump(/*…*/);
… and use it $this->container = require $cache->getPath();
Container Build Register Bundles Register Extensions & Compiler Passes Register
Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers
Cache Warmers public function warmUp($cacheDir) { $filesystem = new Filesystem();
$templates = array(); foreach ($this->finder->findAllTemplates() as $template) { // [snip] } // [snip] $this->writeCacheFile($cacheDir.’/templates.php', // [snip] }
Customize! • You have full access to services, the container
in ready • Create custom caches your application needs • Prefetch and cache data from external sources
None
Thank you :) https://joind.in/talk/bd64e