Slide 1

Slide 1 text

Symfony Internals

Slide 2

Slide 2 text

Symfony Internals Andreas Hucks CTO @ SensioLabs Germany

Slide 3

Slide 3 text

2008

Slide 4

Slide 4 text

Conference Hotel

Slide 5

Slide 5 text

Auditorium

Slide 6

Slide 6 text

Lunch

Slide 7

Slide 7 text

Socials

Slide 8

Slide 8 text

VIP Lounge

Slide 9

Slide 9 text

Wellness

Slide 10

Slide 10 text

Survey

Slide 11

Slide 11 text

Motivation

Slide 12

Slide 12 text

What to expect

Slide 13

Slide 13 text

The Request $kernel = new Kernel($env, $debug); $request = Request::createFromGlobals(); $response = $kernel->handle($request); $response->send();

Slide 14

Slide 14 text

HttpKernel $kernel->handle($request);

Slide 15

Slide 15 text

Request-Response Flow Request Response Front
 Controller Kernel

Slide 16

Slide 16 text

Symfony is not an
 MVC framework

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

Symfony is a
 Request-Response Framework

Slide 19

Slide 19 text

Request-Response Flow Request Response Front
 Controller Kernel

Slide 20

Slide 20 text

Request-Response Flow Kernel

Slide 21

Slide 21 text

Request-Response Flow Kernel kernel.request kernel.controller kernel.response kernel.exception kernel.view kernel.terminate

Slide 22

Slide 22 text

EventDispatcher $dispatcher = new EventDispatcher(); $dispatcher->addListener('kernel.request', $callable);

Slide 23

Slide 23 text

EventDispatcher $dispatcher->dispatch( 'kernel.request', $responseEvent );

Slide 24

Slide 24 text

EventDispatcher $dispatcher->dispatch( 'kernel.request', $responseEvent ); payload What happened.

Slide 25

Slide 25 text

Request-Response Flow Kernel kernel.request kernel.controller kernel.response kernel.exception kernel.view kernel.terminate

Slide 26

Slide 26 text

GetResponseEvent class GetResponseEvent extends KernelEvent { public function getResponse() { return $this->response; } public function setResponse(Response $response) { $this->response = $response; $this->stopPropagation(); } kernel.request

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

RouterListener $parameters = $this ->matcher->match($request->getPathInfo()); $request->attributes->add($parameters); unset( $parameters[‘_route'], $parameters[‘_controller'] ); $request->attributes->set( '_route_params', $parameters ); kernel.request

Slide 29

Slide 29 text

LocaleListener $request = $event->getRequest(); $request->setDefaultLocale($this->defaultLocale); if ($locale = $request->attributes->get('_locale')) { $request->setLocale($locale); } kernel.request

Slide 30

Slide 30 text

Customize! • Content Negotiation - Parse headers and add attribute • Verify signed Cookies • Redirects based on arbitrary criteria • Custom Locale stuff • … ?

Slide 31

Slide 31 text

Request-Response Flow Kernel kernel.request kernel.controller kernel.response kernel.exception kernel.view kernel.terminate

Slide 32

Slide 32 text

FilterControllerEvent class FilterControllerEvent extends KernelEvent { public function getController() { return $this->controller; } public function setController(callable $controller) { $this->controller = $controller; } kernel.controller

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

ParamConverterListener class ParamConverterListener implements EventSubscriberInterface { /** * @var ParamConverterManager */ private $manager; kernel.controller

Slide 35

Slide 35 text

Request-Response Flow Kernel kernel.request kernel.controller kernel.response kernel.exception kernel.view kernel.terminate

Slide 36

Slide 36 text

Wait… Controllers?

Slide 37

Slide 37 text

HttpKernel $response = \call_user_func_array( $controller, $arguments );

Slide 38

Slide 38 text

Request-Response Flow Kernel kernel.request kernel.controller kernel.response kernel.exception kernel.view kernel.terminate

Slide 39

Slide 39 text

GetResponseForControllerResultEvent class GetResponseForControllerResultEvent extends GetResponseEvent { public function getControllerResult() { return $this->controllerResult; } public function setControllerResult($controllerResult) { $this->controllerResult = $controllerResult; } kernel.view

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

Customize! • Auto serializing and encoding for web service responses • Together with kernel.controller: 
 Action Domain Responder, other patterns?

Slide 42

Slide 42 text

Request-Response Flow Kernel kernel.request kernel.controller kernel.response kernel.exception kernel.view kernel.terminate

Slide 43

Slide 43 text

GetResponseForExceptionEvent class GetResponseForExceptionEvent extends GetResponseEvent { public function getException() { return $this->exception; } kernel.exception

Slide 44

Slide 44 text

ExceptionListener public function onKernelException( GetResponseForExceptionEvent $event ) { $exception = $event->getException(); $request = $event->getRequest(); $this->logException($exception kernel.exception

Slide 45

Slide 45 text

Customize! • Customize for every exception type you like • Redirect for some errors • “were you looking for…” suggestions on 404 pages • Custom alerts • … ?

Slide 46

Slide 46 text

Request-Response Flow Kernel kernel.request kernel.controller kernel.response kernel.exception kernel.view kernel.terminate

Slide 47

Slide 47 text

FilterResponseEvent class FilterResponseEvent extends KernelEvent { public function getResponse() { return $this->response; } public function setResponse(Response $response) { $this->response = $response; } kernel.response

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

SaveSessionListener $session = $event->getRequest()->getSession(); if ($session && $session->isStarted()) { $session->save(); $event->getResponse() ->setPrivate() ->setMaxAge(0) ->headers->addCacheControlDirective('must-revalidate'); } kernel.response

Slide 50

Slide 50 text

Customize! • Sign cookies • Add custom headers • … ?

Slide 51

Slide 51 text

Request-Response Flow Kernel kernel.request kernel.controller kernel.response kernel.exception kernel.view kernel.terminate

Slide 52

Slide 52 text

PostResponseEvent class PostResponseEvent extends KernelEvent { public function getResponse() { return $this->response; } kernel.terminate

Slide 53

Slide 53 text

EmailSenderListener public function onTerminate() { // send spooled Emails kernel.terminate

Slide 54

Slide 54 text

Other Listeners kernel.request • FirewallListener • Collector Listeners

Slide 55

Slide 55 text

Recap Request Response Front
 Controller Kernel

Slide 56

Slide 56 text

Container & Cache

Slide 57

Slide 57 text

Container & Cache protected function getRouterService() { $this->services[‘router'] = $instance = new \Symfony\Bundle\…\Router( $this, ‘/app/config/routing_dev.yml' ) // [snip] return $instance; }

Slide 58

Slide 58 text

Container & Cache Cache class for this request? Does it exists? require() it yes Build Container no

Slide 59

Slide 59 text

Container & Cache Cache class for this request? Does it exists? require() it yes Build Container no

Slide 60

Slide 60 text

Container Build Register Bundles Register Extensions & Compiler Passes Register Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers (quite a few)

Slide 61

Slide 61 text

Container Build Register Bundles Register Extensions & Compiler Passes Register Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers

Slide 62

Slide 62 text

Registering Bundles public function registerBundles() { $bundles = [ new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), new Symfony\Bundle\SecurityBundle\SecurityBundle(), new Symfony\Bundle\TwigBundle\TwigBundle(), // [snip] ]; return $bundles; }

Slide 63

Slide 63 text

Container Build Register Bundles Register Extensions & Compiler Passes Register Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers

Slide 64

Slide 64 text

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] }

Slide 65

Slide 65 text

Container Build Register Bundles Register Extensions & Compiler Passes Register Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers

Slide 66

Slide 66 text

Register Project-Level Container Config public function registerContainerConfiguration( LoaderInterface $loader ) { $loader->load( $this->getRootDir(). ’/config/config_’.$this->getEnvironment().'.yml' ); }

Slide 67

Slide 67 text

Container Build Register Bundles Register Extensions & Compiler Passes Register Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers

Slide 68

Slide 68 text

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');

Slide 69

Slide 69 text

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)

Slide 70

Slide 70 text

Container Build Register Bundles Register Extensions & Compiler Passes Register Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers

Slide 71

Slide 71 text

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)) ); }

Slide 72

Slide 72 text

(A few) Compiler Passes • ServiceLocatorTagPass • DecoratorServicePass • ResolveParameterPlaceHoldersPass • ResolveFactoryClassPass • ResolveNamedArgumentsPass • AutowirePass • RemovePrivateAliasesPass • ReplaceAliasByActualDefinitionPass • RemoveAbstractDefinitionsPass • InlineServiceDefinitionsPass • RemoveUnusedDefinitionsPass

Slide 73

Slide 73 text

(A few) Compiler Passes • ServiceLocatorTagPass • DecoratorServicePass • ResolveParameterPlaceHoldersPass • ResolveFactoryClassPass • ResolveNamedArgumentsPass • AutowirePass • RemovePrivateAliasesPass • ReplaceAliasByActualDefinitionPass • RemoveAbstractDefinitionsPass • InlineServiceDefinitionsPass • RemoveUnusedDefinitionsPass

Slide 74

Slide 74 text

Customize! • Add method calls to services based on tags • Manipulate services that depend on the full service config

Slide 75

Slide 75 text

Container Build Register Bundles Register Extensions & Compiler Passes Register Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers

Slide 76

Slide 76 text

Dump Container protected function dumpContainer( ConfigCache $cache, ContainerBuilder $container, $class, $baseClass ) { $dumper = new PhpDumper($container); $content = $dumper->dump(/*…*/);

Slide 77

Slide 77 text

… and use it $this->container = require $cache->getPath();

Slide 78

Slide 78 text

Container Build Register Bundles Register Extensions & Compiler Passes Register Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers

Slide 79

Slide 79 text

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] }

Slide 80

Slide 80 text

Customize! • You have full access to services, the container in ready • Create custom caches your application needs • Prefetch and cache data from external sources

Slide 81

Slide 81 text

No content

Slide 82

Slide 82 text

Thank you :) https://joind.in/talk/bd64e