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

Into the kernel and back

Into the kernel and back

A journey into the heart of the Symfony framework, explaining internals of the application and http kernels, including the rich event ecosystem.

Discover almost unlimited possibilities that come with the framework out of the box, and can be used to extend its default request handling.

Finally, learn how to leverage the kernel and the event dispatcher to migrate away from an Old School PHP Spaghetti Project™ to a modern Symfony based application.

http://www.symfonyday.pt/2014/

1a4e1f98f3aeef310273366c8c785207?s=128

Jakub Zalas

March 08, 2014
Tweet

Transcript

  1. Into the kernel and back Jakub Zalas Lisbon, 8.03.2014

  2. Olá! I work @SensioLabsUK I tweet @jakub_zalas I code @jakzal

  3. HttpKernelInterface

  4. namespace Symfony\Component\HttpKernel; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; interface HttpKernelInterface { const

    MASTER_REQUEST = 1; const SUB_REQUEST = 2; /** * @return Response */ public function handle( Request $request, $type = self::MASTER_REQUEST, $catch = true ); }
  5. namespace Symfony\Component\HttpKernel; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; interface HttpKernelInterface { const

    MASTER_REQUEST = 1; const SUB_REQUEST = 2; /** * @return Response */ public function handle( Request $request, $type = self::MASTER_REQUEST, $catch = true ); }
  6. Request Response /hello/sfdaypt

  7. $response = $kernel->handle($request);

  8. use Symfony\Component\HttpFoundation\Request; $request = Request::createFromGlobals(); $kernel = new SimpleKernel(); $response

    = $kernel->handle($request); $response->send();
  9. use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; class SimpleKernel implements HttpKernelInterface

    { public function handle(Request $request /*,...*/) { return new Response('Hello Portugal'); } }
  10. use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; class SimpleKernel implements HttpKernelInterface

    { public function handle(Request $request /*,...*/) { return new Response('Hello Portugal'); } }
  11. Routing

  12. Request Response callable   callable   callable   callable  

    / /hello/{name} /contact /events Router /hello/sfdaypt
  13. public function handle(Request $request /*,...*/) { $controller = $this->matchRequest($request); $response

    = call_user_func($controller): return $response; } private function matchRequest(Request $request) { $map = array( '/' => array(new Homepage(), 'indexAction'), '/contact' => array(new Contact(), 'formAction') ); return $map[$request->getPathInfo()]; }
  14. public function handle(Request $request /*,...*/) { $controller = $this->matchRequest($request); $response

    = call_user_func($controller): return $response; } private function matchRequest(Request $request) { $map = array( '/' => array(new Homepage(), 'indexAction'), '/contact' => array(new Contact(), 'formAction') ); return $map[$request->getPathInfo()]; }
  15. private function matchRequest(Request $request) { $routes = $this->getRoutes(); $context =

    new RequestContext(); $context->fromRequest($request); $urlMatcher = new UrlMatcher($routes, $context); return $urlMatcher->matchRequest($request); } private function getRoutes() { $routes = new RouteCollection(); $routes->add('homepage', new Route('/', array('_controller' => 'Homepage::indexAction'))); $routes->add('contact', new Route('/', array('_controller' => 'Contact::formAction'))); return $routes; }
  16. private function matchRequest(Request $request) { $routes = $this->getRoutes(); $context =

    new RequestContext(); $context->fromRequest($request); $urlMatcher = new UrlMatcher($routes, $context); return $urlMatcher->matchRequest($request); } private function getRoutes() { $routes = new RouteCollection(); $routes->add('homepage', new Route('/', array('_controller' => 'Homepage::indexAction'))); $routes->add('contact', new Route('/', array('_controller' => 'Contact::formAction'))); return $routes; }
  17. private function matchRequest(Request $request) { $routes = $this->getRoutes(); $context =

    new RequestContext(); $context->fromRequest($request); $urlMatcher = new UrlMatcher($routes, $context); return $urlMatcher->matchRequest($request); } private function getRoutes() { $routes = new RouteCollection(); $routes->add('homepage', new Route('/', array('_controller' => 'Homepage::indexAction'))); $routes->add('contact', new Route('/', array('_controller' => 'Contact::formAction'))); return $routes; }
  18. hello: path: /hello/{name} defaults: _controller: 'DemoController::helloAction'

  19. private function getRoutes() { $locator = new FileLocator(array(__DIR__)); $loader =

    new YamlFileLoader($locator); $routes = $loader->load('routes.yml'); return $routes; }
  20. public function handle(Request $request /*,...*/) { // ['_controller' => 'Homepage::indexAction']

    $parameters = $this->matchRequest($request); // [new Homepage(), 'indexAction'] $controller = ??? $response = call_user_func($controller): return $response; }
  21. Controller resolver

  22. Request Response callable   callable   callable   callable  

    / /hello/{name} /contact /events Router /hello/sfdaypt Controller Resolver
  23. public function handle(Request $request /*,...*/) { $parameters = $this->matchRequest($request); $controller

    = $this->resolveController($parameters); $response = call_user_func($controller): return $response; } private function resolveController($parameters) { // i.e. Homepage::indexAction $controller = $parameters['_controller']; list($class, $method) = explode('::', $controller); $controller = new $class(); return array($class, $method); }
  24. public function handle(Request $request /*,...*/) { $parameters = $this->matchRequest($request); $controller

    = $this->resolveController($parameters); $response = call_user_func($controller): return $response; } private function resolveController($parameters) { // i.e. Homepage::indexAction $controller = $parameters['_controller']; list($class, $method) = explode('::', $controller); $controller = new $class(); return array($class, $method); }
  25. public function handle(Request $request /*,...*/) { $parameters = $this->matchRequest($request); $controller

    = $this->resolveController($parameters); $response = call_user_func($controller): return $response; } private function resolveController($parameters) { // i.e. Homepage::indexAction $controller = $parameters['_controller']; list($class, $method) = explode('::', $controller); $controller = new $class(); return array($class, $method); }
  26. use Symfony\Component\Routing\Matcher\RequestMatcherInterface; use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; public function __construct( RequestMatcherInterface $matcher, ControllerResolverInterface

    $resolver ) { $this->matcher = $matcher; $this->resolver = $resolver; }
  27. public function handle(Request $request /*,...*/) { $parameters = $this->matcher->matchRequest($request); $request->attributes->add($parameters);

    $controller = $this->resolver->resolve($request); $response = call_user_func($controller): return $response; }
  28. public function handle(Request $request /*,...*/) { $parameters = $this->matcher->matchRequest($request); $request->attributes->add($parameters);

    $controller = $this->resolver->resolve($request); $response = call_user_func($controller): return $response; }
  29. EventDispatcherInterface

  30. Handle Request Execute  a   controller   Geoip  lookup  

    Mobile   redirect   Authen6cate   ?   ?   Add     debug  to   response   ?   Match  a   request  
  31. Request Response Router Controller Resolver Event Dispatcher GeoipListener SessionListener LocaleListener

    Firewall kernel.request TemplateListener ControllerListener ParamConverter Listener TemplateListener
  32. Request Response Router Controller Resolver Event Dispatcher GeoipListener SessionListener LocaleListener

    Firewall kernel.request TemplateListener ControllerListener ParamConverter Listener TemplateListener
  33. use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Routing\Matcher\RequestMatcherInterface; use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; public function __construct( EventDispatcherInterface

    $dispatcher, RequestMatcherInterface $matcher, ControllerResolverInterface $resolver ) { $this->eventDispatcher = $dispatcher; $this->matcher = $matcher; $this->resolver = $resolver; }
  34. public function handle(Request $request, $type, $catch) { $event = new

    GetResponseEvent($this, $request, $type); $this->dispatcher->dispatch('kernel.request', $event); $parameters = $this->matcher->matchRequest($request); $request->attributes->add($parameters); $controller = $this->resolver->resolve($request); $response = call_user_func($controller): return $response; }
  35. public function handle(Request $request, $type, $catch) { $event = new

    GetResponseEvent($this, $request, $type); $this->dispatcher->dispatch('kernel.request', $event); $parameters = $this->matcher->matchRequest($request); $request->attributes->add($parameters); $controller = $this->resolver->resolve($request); $response = call_user_func($controller): return $response; }
  36. Request Response Router Controller Resolver Event Dispatcher GeoipListener SessionListener LocaleListener

    Firewall kernel.request
  37. Request Response Controller Resolver Event Dispatcher kernel.request GeoipListener SessionListener LocaleListener

    RouterListener Firewall
  38. use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; public function __construct( EventDispatcherInterface $dispatcher, ControllerResolverInterface

    $resolver ) { $this->eventDispatcher = $dispatcher; $this->resolver = $resolver; }
  39. public function handle(Request $request, $type, $catch) { $event = new

    GetResponseEvent($this, $request, $type); $this->dispatcher->dispatch('kernel.request', $event); $parameters = $this->matcher->matchRequest($request); $request->attributes->add($parameters); $controller = $this->resolver->resolve($request); $response = call_user_func($controller): return $response; }
  40. class RouterListener { public function __construct( RequestMatcherInterface $matcher ) {

    $this->matcher = $matcher } function onKernelRequest(GetResponseEvent $event) { // ... } }
  41. // RouterListener public function onKernelRequest(GetResponseEvent $event) { $request = $event->getRequest();

    if ($request->attributes->has('_controller')) { // routing is already done return; } $parameters = $this->matcher->matchRequest($request); $request->attributes->add($parameters); }
  42. // RouterListener public function onKernelRequest(GetResponseEvent $event) { $request = $event->getRequest();

    if ($request->attributes->has('_controller')) { // routing is already done return; } $parameters = $this->matcher->matchRequest($request); $request->attributes->add($parameters); }
  43. Request Response Controller Resolver Event Dispatcher kernel.request GeoipListener SessionListener LocaleListener

    RouterListener Firewall
  44. Request Response Controller Resolver Event Dispatcher kernel.request GeoipListener SessionListener LocaleListener

    RouterListener Firewall
  45. // RedirectListener public function onKernelRequest(GetResponseEvent $event) { $event->setResponse( new RedirectResponse('http://www.symfonyday.pt/')

    ); }
  46. // HttpKernel public function handle(Request $request, $type, $catch) { $event

    = new GetResponseEvent($this, $request, $type); $this->dispatcher->dispatch('kernel.request', $event); if ($event->hasResponse()) { return $event->getResponse(); } $controller = $this->resolver->resolve($request); $response = call_user_func($controller); return $response; }
  47. Request Response Controller Resolver Event Dispatcher kernel.response CacheListener ResponseListener ProfilerListener

    WebDebugToolbar Listener
  48. Request Response Controller Resolver Event Dispatcher kernel.finish_request LocaleListener RouterListener Firewall

  49. Request Response Controller Resolver Event Dispatcher kernel.finish_request LocaleListener RouterListener Firewall

  50. public function handle(Request $request, $type, $catch) { $event = new

    GetResponseEvent($this, $request, $type); $this->dispatcher->dispatch('kernel.request', $event); if ($event->hasResponse()) { return $this->filterResponse($event->getResponse(), $request, $type); } $controller = $this->resolver->resolve($request); $response = call_user_func($controller); return $this->filterResponse($response, $request, $type); } private function filterResponse(Response $response, Request $request, $type) { $event = new FilterResponseEvent( $this, $request, $type, $response ); $this->dispatcher->dispatch('kernel.response', $event); $response = $event->getResponse(); $event = new FinishRequestEvent($this, $request, $type); $this->dispatcher->dispatch('kernel.finish_request', $event); return $response; }
  51. public function handle(Request $request, $type, $catch) { $event = new

    GetResponseEvent($this, $request, $type); $this->dispatcher->dispatch('kernel.request', $event); if ($event->hasResponse()) { return $this->filterResponse($event->getResponse(), $request, $type); } $controller = $this->resolver->resolve($request); $response = call_user_func($controller); return $this->filterResponse($response, $request, $type); } private function filterResponse(Response $response, Request $request, $type) { $event = new FilterResponseEvent( $this, $request, $type, $response ); $this->dispatcher->dispatch('kernel.response', $event); $response = $event->getResponse(); $event = new FinishRequestEvent($this, $request, $type); $this->dispatcher->dispatch('kernel.finish_request', $event); return $response; }
  52. public function handle(Request $request, $type, $catch) { $event = new

    GetResponseEvent($this, $request, $type); $this->dispatcher->dispatch('kernel.request', $event); if ($event->hasResponse()) { return $this->filterResponse($event->getResponse(), $request, $type); } $controller = $this->resolver->resolve($request); $response = call_user_func($controller); return $this->filterResponse($response, $request, $type); } private function filterResponse(Response $response, Response $response, $type) { $event = new FilterResponseEvent( $this, $request, $type, $response ); $this->dispatcher->dispatch('kernel.response', $event); $response = $event->getResponse(); $event = new FinishRequestEvent($this, $request, $type); $this->dispatcher->dispatch('kernel.finish_request', $event); return $response; }
  53. public function handle(Request $request, $type, $catch) { $event = new

    GetResponseEvent($this, $request, $type); $this->dispatcher->dispatch('kernel.request', $event); if ($event->hasResponse()) { return $this->filterResponse($event->getResponse(), $request, $type); } $controller = $this->resolver->resolve($request); $response = call_user_func($controller); return $this->filterResponse($response, $request, $type); } private function filterResponse(Response $response, Response $response, $type) { $event = new FilterResponseEvent( $this, $request, $type, $response ); $this->dispatcher->dispatch('kernel.response', $event); $response = $event->getResponse(); $event = new FinishRequestEvent($this, $request, $type); $this->dispatcher->dispatch('kernel.finish_request', $event); return $response; }
  54. Request Response Controller Resolver Event Dispatcher ControllerListener kernel.controller RequestDataCollector Listener

    ParamConverter Listener TemplateListener
  55. public function handle(Request $request, $type, $catch) { // ... $controller

    = $this->resolver->resolve($request); $event = new FilterControllerEvent( $this, $controller, $request, $type ); $this->dispatcher->dispatch( 'kernel.controller', $event ); $controller = $event->getController(); $response = call_user_func($controller); return $this->filterResponse($response, $request, $type); }
  56. public function handle(Request $request, $type, $catch) { // ... $controller

    = $this->resolver->resolve($request); $event = new FilterControllerEvent( $this, $controller, $request, $type ); $this->dispatcher->dispatch( 'kernel.controller', $event ); $controller = $event->getController(); $response = call_user_func($controller); return $this->filterResponse($response, $request, $type); }
  57. public function handle(Request $request, $type, $catch) { // ... $controller

    = $this->resolver->resolve($request); $event = new FilterControllerEvent( $this, $controller, $request, $type ); $this->dispatcher->dispatch('kernel.controller', $event); $controller = $event->getController(); $arguments = $this->resolver->getArguments( $request, $controller ); $response = call_user_func($controller, $arguments); return $this->filterResponse($response, $request, $type); }
  58. Request Response Controller Resolver Event Dispatcher TemplateListener kernel.view

  59. public function handle(Request $request, $type, $catch) { // ... $response

    = call_user_func($controller, $arguments); if (!$response instance of Response) { $event = new GetResponseForControllerResultEvent( $this, $request, $type, $response ); $this->dispatcher->dispatch( 'kernel.view', $event ); $response = $event->getResponse(); } return $this->filterResponse($response, $request, $type); }
  60. public function handle(Request $request, $type, $catch) { // ... $response

    = call_user_func($controller, $arguments); if (!$response instance of Response) { $event = new GetResponseForControllerResultEvent( $this, $request, $type, $response ); $this->dispatcher->dispatch( 'kernel.view', $event ); $response = $event->getResponse(); } return $this->filterResponse($response, $request, $type); }
  61. Build-in events

  62. The usual path

  63. Request Response Controller Resolver Event Dispatcher kernel.request FragmentListener SessionListener LocaleListener

    RouterListener Firewall
  64. Request Response Controller Resolver Event Dispatcher ControllerListener kernel.controller RequestDataCollector Listener

    ParamConverter Listener TemplateListener
  65. Request Response Controller Resolver Event Dispatcher kernel.response CacheListener ResponseListener ProfilerListener

    WebDebugToolbar Listener
  66. Request Response Controller Resolver Event Dispatcher kernel.finish_request LocaleListener RouterListener Firewall

  67. Request Response Controller Resolver Event Dispatcher TemplateListener kernel.view

  68. Request Response Controller Resolver Event Dispatcher kernel.terminate ProfilerListener EmailSenderListener

  69. A Response set during kernel.request

  70. Request Response Controller Resolver Event Dispatcher kernel.request FragmentListener SessionListener LocaleListener

    RouterListener Firewall
  71. Request Response Controller Resolver Event Dispatcher kernel.response CacheListener ResponseListener ProfilerListener

    WebDebugToolbar Listener
  72. Request Response Controller Resolver Event Dispatcher kernel.finish_request LocaleListener RouterListener Firewall

  73. Request Response Controller Resolver Event Dispatcher kernel.finish_request LocaleListener RouterListener Firewall

  74. Request Response Controller Resolver Event Dispatcher kernel.terminate TraceableEvent Dispatcher ProfilerListener

    EmailSenderListener
  75. An exception thrown during handling a request

  76. Request Response Controller Resolver Event Dispatcher kernel.request FragmentListener SessionListener LocaleListener

    RouterListener Firewall
  77. Request Response Controller Resolver Event Dispatcher ExceptionListener AccessDeniedListener ProfilerListener kernel.exception

  78. Symfony\Component\HttpKernel\HttpKernel Read more...

  79. KernelInterface

  80. interface KernelInterface extends HttpKernelInterface, \Serializable { public function registerBundles(); public

    function registerContainerConfiguration( LoaderInterface $loader); public function boot(); public function shutdown(); public function getBundles(); public function isClassInActiveBundle($class); public function getBundle($name, $first = true); public function locateResource($name, $dir = null, $first = true); public function getName(); public function getEnvironment(); public function isDebug(); public function getRootDir(); public function getContainer(); public function getStartTime(); public function getCacheDir(); public function getLogDir(); public function getCharset(); }
  81. H<p   Kernel   App  Kernel   Request Response

  82. H<p   Kernel   App  Kernel   Request Response App

     Cache  
  83. class Kernel implements KernelInterface { public function handle( Request $request,

    $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) ) { if (false === $this->booted) { $this->boot(); } return $this->container->get('http_kernel') ->handle($request, $type, $catch); } }
  84. class Kernel implements KernelInterface { public function handle( Request $request,

    $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) ) { if (false === $this->booted) { $this->boot(); } return $this->container->get('http_kernel') ->handle($request, $type, $catch); } }
  85. class Kernel implements KernelInterface { public function handle( Request $request,

    $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) ) { if (false === $this->booted) { $this->boot(); } return $this->container->get('http_kernel') ->handle($request, $type, $catch); } }
  86. public function boot() { $this->initializeBundles(); $this->initializeContainer(); foreach ($this->getBundles() as $bundle)

    { $bundle->setContainer($this->container); $bundle->boot(); } $this->booted = true; }
  87. protected function initializeContainer() { // ... if (!$cache->isFresh()) { $container

    = $this->buildContainer(); $container->compile(); $this->dumpContainer($cache, $container/*…*/); } require_once $cache; $this->container = new $class(); $this->container->set('kernel', $this); // ... }
  88. protected function initializeContainer() { // ... if (!$cache->isFresh()) { $container

    = $this->buildContainer(); $container->compile(); $this->dumpContainer($cache, $container/*…*/); } require_once $cache; $this->container = new $class(); $this->container->set('kernel', $this); // ... }
  89. class appDevDebugProjectContainer extends Container { // ... protected function getLoggerService()

    { $this->services['logger'] = $instance = new \Symfony\Bridge\Monolog\Logger('app'); $instance->pushHandler($this->get('monolog.handler.console')); $instance->pushHandler($this->get('monolog.handler.main')); $instance->pushHandler($this->get('monolog.handler.debug')); return $instance; } // ... }
  90. protected function initializeContainer() { // ... if (!$cache->isFresh()) { $container

    = $this->buildContainer(); $container->compile(); $this->dumpContainer($cache, $container/*…*/); } require_once $cache; $this->container = new $class(); $this->container->set('kernel', $this); // ... }
  91. protected function initializeContainer() { // ... if (!$cache->isFresh()) { $container

    = $this->buildContainer(); $container->compile(); $this->dumpContainer($cache, $container/*…*/); } require_once $cache; $this->container = new $class(); $this->container->set('kernel', $this); // ... }
  92. Container and events in a legacy application

  93. $kernel = new \AppKernel('dev', true); $kernel->boot(); $request = Request::createFromGlobals(); $request->attributes->set('is_legacy',

    true); $container = $kernel->getContainer(); $container->enterScope('request'); $container->get('request_stack')->push($request); $event = new GetResponseEvent( $kernel, $request, HttpKernelInterface::MASTER_REQUEST ); $container->get('event_dispatcher'); ->dispatch('kernel.request', $event);
  94. $kernel = new \AppKernel('dev', true); $kernel->boot(); $request = Request::createFromGlobals(); $request->attributes->set('is_legacy',

    true); $container = $kernel->getContainer(); $container->enterScope('request'); $container->get('request_stack')->push($request); $event = new GetResponseEvent( $kernel, $request, HttpKernelInterface::MASTER_REQUEST ); $container->get('event_dispatcher'); ->dispatch('kernel.request', $event);
  95. $kernel = new \AppKernel('dev', true); $kernel->boot(); $request = Request::createFromGlobals(); $request->attributes->set('is_legacy',

    true); $container = $kernel->getContainer(); $container->enterScope('request'); $container->get('request_stack')->push($request); $event = new GetResponseEvent( $kernel, $request, HttpKernelInterface::MASTER_REQUEST ); $container->get('event_dispatcher'); ->dispatch('kernel.request', $event);
  96. $kernel = new \AppKernel('dev', true); $kernel->boot(); $request = Request::createFromGlobals(); $request->attributes->set('is_legacy',

    true); $container = $kernel->getContainer(); $container->enterScope('request'); $container->get('request_stack')->push($request); $event = new GetResponseEvent( $kernel, $request, HttpKernelInterface::MASTER_REQUEST ); $container->get('event_dispatcher'); ->dispatch('kernel.request', $event);
  97. $kernel = new \AppKernel('dev', true); $kernel->boot(); $request = Request::createFromGlobals(); $request->attributes->set('is_legacy',

    true); $container = $kernel->getContainer(); $container->enterScope('request'); $container->get('request_stack')->push($request); $event = new GetResponseEvent( $kernel, $request, HttpKernelInterface::MASTER_REQUEST ); $container->get('event_dispatcher'); ->dispatch('kernel.request', $event);
  98. class RouterListener extends BaseRouterListener { public function onKernelRequest( GetResponseEvent $event

    ) { $request = $event->getRequest(); try { parent::onKernelRequest($event); } catch (NotFoundHttpException $e) { if (!$request->attributes->get('is_legacy')) { throw $e; } } } }
  99. Thank you! https://joind.in/10782