vendor/sensio/framework-extra-bundle/src/EventListener/TemplateListener.php line 54

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Sensio\Bundle\FrameworkExtraBundle\EventListener;
  11. use Psr\Container\ContainerInterface;
  12. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
  13. use Sensio\Bundle\FrameworkExtraBundle\Templating\TemplateGuesser;
  14. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  15. use Symfony\Component\HttpFoundation\Request;
  16. use Symfony\Component\HttpFoundation\Response;
  17. use Symfony\Component\HttpFoundation\StreamedResponse;
  18. use Symfony\Component\HttpKernel\Event\KernelEvent;
  19. use Symfony\Component\HttpKernel\KernelEvents;
  20. use Symfony\Contracts\Service\ServiceSubscriberInterface;
  21. use Twig\Environment;
  22. /**
  23. * Handles the Template annotation for actions.
  24. *
  25. * Depends on pre-processing of the ControllerListener.
  26. *
  27. * @author Fabien Potencier <fabien@symfony.com>
  28. */
  29. class TemplateListener implements EventSubscriberInterface, ServiceSubscriberInterface
  30. {
  31. private $templateGuesser;
  32. private $twig;
  33. private $container;
  34. public function __construct(TemplateGuesser $templateGuesser, Environment $twig = null)
  35. {
  36. $this->templateGuesser = $templateGuesser;
  37. $this->twig = $twig;
  38. }
  39. public function setContainer(ContainerInterface $container): void
  40. {
  41. $this->container = $container;
  42. }
  43. /**
  44. * Guesses the template name to render and its variables and adds them to
  45. * the request object.
  46. */
  47. public function onKernelController(KernelEvent $event)
  48. {
  49. $request = $event->getRequest();
  50. $template = $request->attributes->get('_template');
  51. if (!$template instanceof Template) {
  52. return;
  53. }
  54. $controller = $event->getController();
  55. if (!\is_array($controller) && method_exists($controller, '__invoke')) {
  56. $controller = [$controller, '__invoke'];
  57. }
  58. $template->setOwner($controller);
  59. // when no template has been given, try to resolve it based on the controller
  60. if (null === $template->getTemplate()) {
  61. $template->setTemplate($this->templateGuesser->guessTemplateName($controller, $request));
  62. }
  63. }
  64. /**
  65. * Renders the template and initializes a new response object with the
  66. * rendered template content.
  67. */
  68. public function onKernelView(KernelEvent $event)
  69. {
  70. /* @var Template $template */
  71. $request = $event->getRequest();
  72. $template = $request->attributes->get('_template');
  73. if (!$template instanceof Template) {
  74. return;
  75. }
  76. if (null === $this->twig) {
  77. if (!$this->container || !$this->container->has('twig')) {
  78. throw new \LogicException('You can not use the "@Template" annotation if the Twig Bundle is not available.');
  79. }
  80. $this->twig = $this->container->get('twig');
  81. }
  82. $parameters = $event->getControllerResult();
  83. $owner = $template->getOwner();
  84. list($controller, $action) = $owner;
  85. // when the annotation declares no default vars and the action returns
  86. // null, all action method arguments are used as default vars
  87. if (null === $parameters) {
  88. $parameters = $this->resolveDefaultParameters($request, $template, $controller, $action);
  89. }
  90. // attempt to render the actual response
  91. if ($template->isStreamable()) {
  92. $callback = function () use ($template, $parameters) {
  93. $this->twig->display($template->getTemplate(), $parameters);
  94. };
  95. $event->setResponse(new StreamedResponse($callback));
  96. } else {
  97. $event->setResponse(new Response($this->twig->render($template->getTemplate(), $parameters)));
  98. }
  99. // make sure the owner (controller+dependencies) is not cached or stored elsewhere
  100. $template->setOwner([]);
  101. }
  102. /**
  103. * {@inheritdoc}
  104. */
  105. public static function getSubscribedEvents()
  106. {
  107. return [
  108. KernelEvents::CONTROLLER => ['onKernelController', -128],
  109. KernelEvents::VIEW => 'onKernelView',
  110. ];
  111. }
  112. public static function getSubscribedServices(): array
  113. {
  114. return ['twig' => '?'.Environment::class];
  115. }
  116. private function resolveDefaultParameters(Request $request, Template $template, $controller, $action)
  117. {
  118. $parameters = [];
  119. $arguments = $template->getVars();
  120. if (0 === \count($arguments)) {
  121. $r = new \ReflectionObject($controller);
  122. $arguments = [];
  123. foreach ($r->getMethod($action)->getParameters() as $param) {
  124. $arguments[] = $param;
  125. }
  126. }
  127. // fetch the arguments of @Template.vars or everything if desired
  128. // and assign them to the designated template
  129. foreach ($arguments as $argument) {
  130. if ($argument instanceof \ReflectionParameter) {
  131. $parameters[$name = $argument->getName()] = !$request->attributes->has($name) && $argument->isDefaultValueAvailable() ? $argument->getDefaultValue() : $request->attributes->get($name);
  132. } else {
  133. $parameters[$argument] = $request->attributes->get($argument);
  134. }
  135. }
  136. return $parameters;
  137. }
  138. }