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/

Jakub Zalas

March 08, 2014
Tweet

More Decks by Jakub Zalas

Other Decks in Technology

Transcript

  1. 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 ); }
  2. 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 ); }
  3. Request Response callable   callable   callable   callable  

    / /hello/{name} /contact /events Router /hello/sfdaypt
  4. 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()]; }
  5. 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()]; }
  6. 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; }
  7. 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; }
  8. 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; }
  9. private function getRoutes() { $locator = new FileLocator(array(__DIR__)); $loader =

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

    $parameters = $this->matchRequest($request); // [new Homepage(), 'indexAction'] $controller = ??? $response = call_user_func($controller): return $response; }
  11. Request Response callable   callable   callable   callable  

    / /hello/{name} /contact /events Router /hello/sfdaypt Controller Resolver
  12. 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); }
  13. 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); }
  14. 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); }
  15. 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; }
  16. 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; }
  17. Handle Request Execute  a   controller   Geoip  lookup  

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

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

    Firewall kernel.request TemplateListener ControllerListener ParamConverter Listener TemplateListener
  20. 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; }
  21. 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; }
  22. 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; }
  23. 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; }
  24. class RouterListener { public function __construct( RequestMatcherInterface $matcher ) {

    $this->matcher = $matcher } function onKernelRequest(GetResponseEvent $event) { // ... } }
  25. // 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); }
  26. // 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); }
  27. // 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; }
  28. 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; }
  29. 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; }
  30. 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; }
  31. 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; }
  32. 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); }
  33. 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); }
  34. 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); }
  35. 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); }
  36. 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); }
  37. 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(); }
  38. 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); } }
  39. 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); } }
  40. 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); } }
  41. public function boot() { $this->initializeBundles(); $this->initializeContainer(); foreach ($this->getBundles() as $bundle)

    { $bundle->setContainer($this->container); $bundle->boot(); } $this->booted = true; }
  42. 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); // ... }
  43. 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); // ... }
  44. 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; } // ... }
  45. 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); // ... }
  46. 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); // ... }
  47. $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);
  48. $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);
  49. $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);
  50. $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);
  51. $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);
  52. 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; } } } }