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
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Andreas Hucks
January 27, 2018
Programming
960
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Symfony Internals
PHPBenelux 2018
Andreas Hucks
January 27, 2018
More Decks by Andreas Hucks
See All by Andreas Hucks
Divide and Conquer (LonghornPHP 2019)
meandmymonkey
0
220
Divide and Conquer
meandmymonkey
1
760
Deptrac - Keep Your Architecture Clean
meandmymonkey
0
850
Introduction to Docker at PHPBenelux2015
meandmymonkey
3
950
Best Practices in Symfony2
meandmymonkey
0
550
Introduction to Docker at PHPNW2014
meandmymonkey
4
440
O(ops), Authentication!
meandmymonkey
4
1k
Best Practices in Symfony2
meandmymonkey
15
1.9k
Best Practices in Symfony2
meandmymonkey
28
4.7k
Other Decks in Programming
See All in Programming
私のClaude Code活用法 (個人開発編) - PHPerKaigi mini #4(2026/08/24)
panda_program
1
210
異なる設計思想のフレームワークを経験して得た学び
amekuhideki
2
1.5k
ソフトウェアラスタライザ
fadis
1
780
週末にAI-DLCを本気で回したら$1,600溶けた
hbashimizu
0
120
レビュー履歴をAIに食わせて、 Compose移行を加速するs
shihochan
0
260
in-process GraphQL のすすめ #ginzajs
izumin5210
4
1.5k
Discordを用いたラボオートメーション関連情報収集の自動化
noguhiro2002
0
490
FastAPI の並行処理モデルを完全に理解する
hoto17296
9
3.7k
The Good Stuff, Not the Slop: Engineering High-Quality Android Apps with Modern AI Tooling
danybony
0
120
Building an Out-of-Order CPU
latte72
0
620
LLMは4年分のCompose移行を再現できるのか?実プロダクト279件のXMLで探る自動化の境界線
makun
0
350
tsc.rip を支える技術 / Kyoto.なんか #8
susisu
0
4.3k
Featured
See All Featured
Lessons Learnt from Crawling 1000+ Websites
charlesmeaden
PRO
1
1.6k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
25
2k
Utilizing Notion as your number one productivity tool
mfonobong
4
570
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
67
58k
Scaling GitHub
holman
464
140k
Skip the Path - Find Your Career Trail
mkilby
1
220
Heart Work Chapter 1 - Part 1
lfama
PRO
8
36k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Marketing Yourself as an Engineer | Alaka | Gurzu
gurzu
0
290
The Illustrated Children's Guide to Kubernetes
chrisshort
51
53k
Unsuck your backbone
ammeep
672
58k
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