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