Lock in $30 Savings on PRO—Offer Ends Soon! ⏳
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Symfony Internals
Search
Andreas Hucks
January 27, 2018
Programming
3
900
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
170
Divide and Conquer
meandmymonkey
1
690
Deptrac - Keep Your Architecture Clean
meandmymonkey
0
740
Introduction to Docker at PHPBenelux2015
meandmymonkey
3
880
Best Practices in Symfony2
meandmymonkey
0
460
Introduction to Docker at PHPNW2014
meandmymonkey
4
410
O(ops), Authentication!
meandmymonkey
4
960
Best Practices in Symfony2
meandmymonkey
15
1.8k
Best Practices in Symfony2
meandmymonkey
28
4.6k
Other Decks in Programming
See All in Programming
Building AI Agents with TypeScript #TSKaigiHokuriku
izumin5210
6
1.2k
モダンJSフレームワークのビルドプロセス 〜なぜReactは503行、Svelteは12行なのか〜
fuuki12
0
210
AIと協働し、イベントソーシングとアクターモデルで作る後悔しないアーキテクチャ Regret-Free Architecture with AI, Event Sourcing, and Actors
tomohisa
5
19k
新卒エンジニアのプルリクエスト with AI駆動
fukunaga2025
0
180
ローターアクトEクラブ アメリカンナイト:川端 柚菜 氏(Japan O.K. ローターアクトEクラブ 会長):2720 Japan O.K. ロータリーEクラブ2025年12月1日卓話
2720japanoke
0
710
令和最新版Android Studioで化石デバイス向けアプリを作る
arkw
0
340
AIエンジニアリングのご紹介 / Introduction to AI Engineering
rkaga
4
1.4k
[SF Ruby Conf 2025] Rails X
palkan
0
470
WebRTC と Rust と8K 60fps
tnoho
2
1.9k
社内オペレーション改善のためのTypeScript / TSKaigi Hokuriku 2025
dachi023
1
540
S3 VectorsとStrands Agentsを利用したAgentic RAGシステムの構築
tosuri13
6
290
LLMで複雑な検索条件アセットから脱却する!! 生成的検索インタフェースの設計論
po3rin
1
410
Featured
See All Featured
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
1.8k
For a Future-Friendly Web
brad_frost
180
10k
Building a Scalable Design System with Sketch
lauravandoore
463
34k
Making the Leap to Tech Lead
cromwellryan
135
9.7k
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
380
Thoughts on Productivity
jonyablonski
73
5k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.3k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
120
20k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
9
1.1k
Stop Working from a Prison Cell
hatefulcrawdad
273
21k
Facilitating Awesome Meetings
lara
57
6.7k
Practical Orchestrator
shlominoach
190
11k
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