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
720
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
99
Divide and Conquer
meandmymonkey
1
600
Deptrac - Keep Your Architecture Clean
meandmymonkey
0
580
Introduction to Docker at PHPBenelux2015
meandmymonkey
3
780
Best Practices in Symfony2
meandmymonkey
0
320
Introduction to Docker at PHPNW2014
meandmymonkey
4
390
O(ops), Authentication!
meandmymonkey
4
770
Best Practices in Symfony2
meandmymonkey
15
1.7k
Best Practices in Symfony2
meandmymonkey
28
4.5k
Other Decks in Programming
See All in Programming
カスタムしながら理解するGraphQL Connection
yanagii
1
1.5k
Streams APIとTCPフロー制御 / Web Streams API and TCP flow control
tasshi
2
340
JaSST 24 九州:ワークショップ(は除く)実践!マインドマップを活用したソフトウェアテスト+活用事例
satohiroyuki
0
460
EventSourcingの理想と現実
wenas
6
2.2k
Pinia Colada が実現するスマートな非同期処理
naokihaba
4
210
Compose 1.7のTextFieldはPOBox Plusで日本語変換できない
tomoya0x00
0
170
ヤプリ新卒SREの オンボーディング
masaki12
0
110
CSC509 Lecture 09
javiergs
PRO
0
140
プロジェクト新規参入者のリードタイム短縮の観点から見る、品質の高いコードとアーキテクチャを保つメリット
d_endo
1
1.1k
광고 소재 심사 과정에 AI를 도입하여 광고 서비스 생산성 향상시키기
kakao
PRO
0
160
「今のプロジェクトいろいろ大変なんですよ、app/services とかもあって……」/After Kaigi on Rails 2024 LT Night
junk0612
4
2k
破壊せよ!データ破壊駆動で考えるドメインモデリング / data-destroy-driven
minodriven
17
4.3k
Featured
See All Featured
The Cult of Friendly URLs
andyhume
78
6k
The Straight Up "How To Draw Better" Workshop
denniskardys
232
140k
Designing the Hi-DPI Web
ddemaree
280
34k
The MySQL Ecosystem @ GitHub 2015
samlambert
250
12k
Done Done
chrislema
181
16k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
47
5k
Bootstrapping a Software Product
garrettdimon
PRO
305
110k
The Art of Programming - Codeland 2020
erikaheidi
52
13k
Unsuck your backbone
ammeep
668
57k
Build your cross-platform service in a week with App Engine
jlugia
229
18k
Rebuilding a faster, lazier Slack
samanthasiow
79
8.7k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
231
17k
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