HttpKernel.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. <?php
  2. namespace Symfony\Bundle\FrameworkBundle;
  3. use Symfony\Component\HttpFoundation\Request;
  4. use Symfony\Component\HttpKernel\HttpKernelInterface;
  5. use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
  6. use Symfony\Component\DependencyInjection\ContainerInterface;
  7. use Symfony\Component\HttpKernel\HttpKernel as BaseHttpKernel;
  8. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  9. /**
  10. * This HttpKernel is used to manage scope changes of the DI container.
  11. *
  12. * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  13. */
  14. class HttpKernel extends BaseHttpKernel
  15. {
  16. private $container;
  17. private $esiSupport;
  18. public function __construct(EventDispatcherInterface $dispatcher, ContainerInterface $container, ControllerResolverInterface $controllerResolver)
  19. {
  20. parent::__construct($dispatcher, $controllerResolver);
  21. $this->container = $container;
  22. }
  23. public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
  24. {
  25. $this->container->enterScope('request');
  26. $this->container->set('request', $request, 'request');
  27. try {
  28. $response = parent::handle($request, $type, $catch);
  29. } catch (\Exception $e) {
  30. $this->container->leaveScope('request');
  31. throw $e;
  32. }
  33. $this->container->leaveScope('request');
  34. return $response;
  35. }
  36. /**
  37. * Forwards the request to another controller.
  38. *
  39. * @param string $controller The controller name (a string like Blog:Post:index)
  40. * @param array $attributes An array of request attributes
  41. * @param array $query An array of request query parameters
  42. *
  43. * @return Response A Response instance
  44. */
  45. public function forward($controller, array $attributes = array(), array $query = array())
  46. {
  47. $attributes['_controller'] = $controller;
  48. $subRequest = $this->container->get('request')->duplicate($query, null, $attributes);
  49. return $this->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
  50. }
  51. /**
  52. * Renders a Controller and returns the Response content.
  53. *
  54. * Note that this method generates an esi:include tag only when both the standalone
  55. * option is set to true and the request has ESI capability (@see Symfony\Component\HttpKernel\HttpCache\ESI).
  56. *
  57. * Available options:
  58. *
  59. * * attributes: An array of request attributes (only when the first argument is a controller)
  60. * * query: An array of request query parameters (only when the first argument is a controller)
  61. * * ignore_errors: true to return an empty string in case of an error
  62. * * alt: an alternative controller to execute in case of an error (can be a controller, a URI, or an array with the controller, the attributes, and the query arguments)
  63. * * standalone: whether to generate an esi:include tag or not when ESI is supported
  64. * * comment: a comment to add when returning an esi:include tag
  65. *
  66. * @param string $controller A controller name to execute (a string like Blog:Post:index), or a relative URI
  67. * @param array $options An array of options
  68. *
  69. * @return string The Response content
  70. */
  71. public function render($controller, array $options = array())
  72. {
  73. $options = array_merge(array(
  74. 'attributes' => array(),
  75. 'query' => array(),
  76. 'ignore_errors' => !$this->container->getParameter('kernel.debug'),
  77. 'alt' => array(),
  78. 'standalone' => false,
  79. 'comment' => '',
  80. ), $options);
  81. if (!is_array($options['alt'])) {
  82. $options['alt'] = array($options['alt']);
  83. }
  84. if (null === $this->esiSupport) {
  85. $this->esiSupport = $this->container->has('esi') && $this->container->get('esi')->hasSurrogateEsiCapability($this->container->get('request'));
  86. }
  87. if ($this->esiSupport && $options['standalone']) {
  88. $uri = $this->generateInternalUri($controller, $options['attributes'], $options['query']);
  89. $alt = '';
  90. if ($options['alt']) {
  91. $alt = $this->generateInternalUri($options['alt'][0], isset($options['alt'][1]) ? $options['alt'][1] : array(), isset($options['alt'][2]) ? $options['alt'][2] : array());
  92. }
  93. return $this->container->get('esi')->renderIncludeTag($uri, $alt, $options['ignore_errors'], $options['comment']);
  94. }
  95. $request = $this->container->get('request');
  96. // controller or URI?
  97. if (0 === strpos($controller, '/')) {
  98. $subRequest = Request::create($controller, 'get', array(), $request->cookies->all(), array(), $request->server->all());
  99. $subRequest->setSession($request->getSession());
  100. } else {
  101. $options['attributes']['_controller'] = $controller;
  102. $options['attributes']['_format'] = $request->getRequestFormat();
  103. $options['attributes']['_route'] = '_internal';
  104. $subRequest = $request->duplicate($options['query'], null, $options['attributes']);
  105. }
  106. try {
  107. $response = $this->handle($subRequest, HttpKernelInterface::SUB_REQUEST, false);
  108. if (!$response->isSuccessful()) {
  109. throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $request->getUri(), $response->getStatusCode()));
  110. }
  111. return $response->getContent();
  112. } catch (\Exception $e) {
  113. if ($options['alt']) {
  114. $alt = $options['alt'];
  115. unset($options['alt']);
  116. $options['attributes'] = isset($alt[1]) ? $alt[1] : array();
  117. $options['query'] = isset($alt[2]) ? $alt[2] : array();
  118. return $this->render($alt[0], $options);
  119. }
  120. if (!$options['ignore_errors']) {
  121. throw $e;
  122. }
  123. }
  124. }
  125. /**
  126. * Generates an internal URI for a given controller.
  127. *
  128. * This method uses the "_internal" route, which should be available.
  129. *
  130. * @param string $controller A controller name to execute (a string like Blog:Post:index), or a relative URI
  131. * @param array $attributes An array of request attributes
  132. * @param array $query An array of request query parameters
  133. *
  134. * @return string An internal URI
  135. */
  136. public function generateInternalUri($controller, array $attributes = array(), array $query = array())
  137. {
  138. if (0 === strpos($controller, '/')) {
  139. return $controller;
  140. }
  141. $uri = $this->container->get('router')->generate('_internal', array(
  142. 'controller' => $controller,
  143. 'path' => $attributes ? http_build_query($attributes) : 'none',
  144. '_format' => $this->container->get('request')->getRequestFormat(),
  145. ), true);
  146. if ($query) {
  147. $uri = $uri.'?'.http_build_query($query);
  148. }
  149. return $uri;
  150. }
  151. }