Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Symfony Internals

Symfony Internals

PHPBenelux 2018

Andreas Hucks

January 27, 2018
Tweet

More Decks by Andreas Hucks

Other Decks in Programming

Transcript

  1. Symfony Internals

  2. Symfony Internals Andreas Hucks CTO @ SensioLabs Germany

  3. 2008

  4. Conference Hotel

  5. Auditorium

  6. Lunch

  7. Socials

  8. VIP Lounge

  9. Wellness

  10. Survey

  11. Motivation

  12. What to expect

  13. The Request $kernel = new Kernel($env, $debug); $request = Request::createFromGlobals();

    $response = $kernel->handle($request); $response->send();
  14. HttpKernel $kernel->handle($request);

  15. Request-Response Flow Request Response Front
 Controller Kernel

  16. Symfony is not an
 MVC framework

  17. None
  18. Symfony is a
 Request-Response Framework

  19. Request-Response Flow Request Response Front
 Controller Kernel

  20. Request-Response Flow Kernel

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

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

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

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

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

  26. GetResponseEvent class GetResponseEvent extends KernelEvent { public function getResponse() {

    return $this->response; } public function setResponse(Response $response) { $this->response = $response; $this->stopPropagation(); } kernel.request
  27. 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
  28. RouterListener $parameters = $this ->matcher->match($request->getPathInfo()); $request->attributes->add($parameters); unset( $parameters[‘_route'], $parameters[‘_controller'] );

    $request->attributes->set( '_route_params', $parameters ); kernel.request
  29. LocaleListener $request = $event->getRequest(); $request->setDefaultLocale($this->defaultLocale); if ($locale = $request->attributes->get('_locale')) {

    $request->setLocale($locale); } kernel.request
  30. Customize! • Content Negotiation - Parse headers and add attribute

    • Verify signed Cookies • Redirects based on arbitrary criteria • Custom Locale stuff • … ?
  31. Request-Response Flow Kernel kernel.request kernel.controller kernel.response kernel.exception kernel.view kernel.terminate

  32. FilterControllerEvent class FilterControllerEvent extends KernelEvent { public function getController() {

    return $this->controller; } public function setController(callable $controller) { $this->controller = $controller; } kernel.controller
  33. 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
  34. ParamConverterListener class ParamConverterListener implements EventSubscriberInterface { /** * @var ParamConverterManager

    */ private $manager; kernel.controller
  35. Request-Response Flow Kernel kernel.request kernel.controller kernel.response kernel.exception kernel.view kernel.terminate

  36. Wait… Controllers?

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

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

  39. GetResponseForControllerResultEvent class GetResponseForControllerResultEvent extends GetResponseEvent { public function getControllerResult() {

    return $this->controllerResult; } public function setControllerResult($controllerResult) { $this->controllerResult = $controllerResult; } kernel.view
  40. 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
  41. Customize! • Auto serializing and encoding for web service responses

    • Together with kernel.controller: 
 Action Domain Responder, other patterns?
  42. Request-Response Flow Kernel kernel.request kernel.controller kernel.response kernel.exception kernel.view kernel.terminate

  43. GetResponseForExceptionEvent class GetResponseForExceptionEvent extends GetResponseEvent { public function getException() {

    return $this->exception; } kernel.exception
  44. ExceptionListener public function onKernelException( GetResponseForExceptionEvent $event ) { $exception =

    $event->getException(); $request = $event->getRequest(); $this->logException($exception kernel.exception
  45. Customize! • Customize for every exception type you like •

    Redirect for some errors • “were you looking for…” suggestions on 404 pages • Custom alerts • … ?
  46. Request-Response Flow Kernel kernel.request kernel.controller kernel.response kernel.exception kernel.view kernel.terminate

  47. FilterResponseEvent class FilterResponseEvent extends KernelEvent { public function getResponse() {

    return $this->response; } public function setResponse(Response $response) { $this->response = $response; } kernel.response
  48. 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
  49. SaveSessionListener $session = $event->getRequest()->getSession(); if ($session && $session->isStarted()) { $session->save();

    $event->getResponse() ->setPrivate() ->setMaxAge(0) ->headers->addCacheControlDirective('must-revalidate'); } kernel.response
  50. Customize! • Sign cookies • Add custom headers • …

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

  52. PostResponseEvent class PostResponseEvent extends KernelEvent { public function getResponse() {

    return $this->response; } kernel.terminate
  53. EmailSenderListener public function onTerminate() { // send spooled Emails kernel.terminate

  54. Other Listeners kernel.request • FirewallListener • Collector Listeners

  55. Recap Request Response Front
 Controller Kernel

  56. Container & Cache

  57. Container & Cache protected function getRouterService() { $this->services[‘router'] = $instance

    = new \Symfony\Bundle\…\Router( $this, ‘/app/config/routing_dev.yml' ) // [snip] return $instance; }
  58. Container & Cache Cache class for this request? Does it

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

    exists? require() it yes Build Container no
  60. Container Build Register Bundles Register Extensions & Compiler Passes Register

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

    Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers
  62. Registering Bundles public function registerBundles() { $bundles = [ new

    Symfony\Bundle\FrameworkBundle\FrameworkBundle(), new Symfony\Bundle\SecurityBundle\SecurityBundle(), new Symfony\Bundle\TwigBundle\TwigBundle(), // [snip] ]; return $bundles; }
  63. Container Build Register Bundles Register Extensions & Compiler Passes Register

    Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers
  64. 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] }
  65. Container Build Register Bundles Register Extensions & Compiler Passes Register

    Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers
  66. Register Project-Level Container Config public function registerContainerConfiguration( LoaderInterface $loader )

    { $loader->load( $this->getRootDir(). ’/config/config_’.$this->getEnvironment().'.yml' ); }
  67. Container Build Register Bundles Register Extensions & Compiler Passes Register

    Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers
  68. 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');
  69. 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)
  70. Container Build Register Bundles Register Extensions & Compiler Passes Register

    Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers
  71. 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)) ); }
  72. (A few) Compiler Passes • ServiceLocatorTagPass • DecoratorServicePass • ResolveParameterPlaceHoldersPass

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

    • ResolveFactoryClassPass • ResolveNamedArgumentsPass • AutowirePass • RemovePrivateAliasesPass • ReplaceAliasByActualDefinitionPass • RemoveAbstractDefinitionsPass • InlineServiceDefinitionsPass • RemoveUnusedDefinitionsPass
  74. Customize! • Add method calls to services based on tags

    • Manipulate services that depend on the full service config
  75. Container Build Register Bundles Register Extensions & Compiler Passes Register

    Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers
  76. Dump Container protected function dumpContainer( ConfigCache $cache, ContainerBuilder $container, $class,

    $baseClass ) { $dumper = new PhpDumper($container); $content = $dumper->dump(/*…*/);
  77. … and use it $this->container = require $cache->getPath();

  78. Container Build Register Bundles Register Extensions & Compiler Passes Register

    Project Config Run Extensions Run compiler Passes Dump Container Run Cache Warmers
  79. 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] }
  80. Customize! • You have full access to services, the container

    in ready • Create custom caches your application needs • Prefetch and cache data from external sources
  81. None
  82. Thank you :) https://joind.in/talk/bd64e