Save 37% off PRO during our Black Friday Sale! »

Symfony Internals

Symfony Internals

PHPBenelux 2018

E9612cd342dbddff6640b99db21deee7?s=128

Andreas Hucks

January 27, 2018
Tweet

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