|
1 <?php |
|
2 |
|
3 /* |
|
4 * This file is part of the Symfony package. |
|
5 * |
|
6 * (c) Fabien Potencier <fabien@symfony.com> |
|
7 * |
|
8 * For the full copyright and license information, please view the LICENSE |
|
9 * file that was distributed with this source code. |
|
10 */ |
|
11 |
|
12 namespace Symfony\Component\HttpKernel; |
|
13 |
|
14 use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; |
|
15 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; |
|
16 use Symfony\Component\HttpKernel\Event\FilterControllerEvent; |
|
17 use Symfony\Component\HttpKernel\Event\FilterResponseEvent; |
|
18 use Symfony\Component\HttpKernel\Event\GetResponseEvent; |
|
19 use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent; |
|
20 use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; |
|
21 use Symfony\Component\HttpFoundation\Request; |
|
22 use Symfony\Component\HttpFoundation\Response; |
|
23 use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
|
24 |
|
25 /** |
|
26 * HttpKernel notifies events to convert a Request object to a Response one. |
|
27 * |
|
28 * @author Fabien Potencier <fabien@symfony.com> |
|
29 * |
|
30 * @api |
|
31 */ |
|
32 class HttpKernel implements HttpKernelInterface |
|
33 { |
|
34 private $dispatcher; |
|
35 private $resolver; |
|
36 |
|
37 /** |
|
38 * Constructor |
|
39 * |
|
40 * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance |
|
41 * @param ControllerResolverInterface $resolver A ControllerResolverInterface instance |
|
42 * |
|
43 * @api |
|
44 */ |
|
45 public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver) |
|
46 { |
|
47 $this->dispatcher = $dispatcher; |
|
48 $this->resolver = $resolver; |
|
49 } |
|
50 |
|
51 /** |
|
52 * Handles a Request to convert it to a Response. |
|
53 * |
|
54 * When $catch is true, the implementation must catch all exceptions |
|
55 * and do its best to convert them to a Response instance. |
|
56 * |
|
57 * @param Request $request A Request instance |
|
58 * @param integer $type The type of the request |
|
59 * (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST) |
|
60 * @param Boolean $catch Whether to catch exceptions or not |
|
61 * |
|
62 * @return Response A Response instance |
|
63 * |
|
64 * @throws \Exception When an Exception occurs during processing |
|
65 * |
|
66 * @api |
|
67 */ |
|
68 public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) |
|
69 { |
|
70 try { |
|
71 return $this->handleRaw($request, $type); |
|
72 } catch (\Exception $e) { |
|
73 if (false === $catch) { |
|
74 throw $e; |
|
75 } |
|
76 |
|
77 return $this->handleException($e, $request, $type); |
|
78 } |
|
79 } |
|
80 |
|
81 /** |
|
82 * Handles a request to convert it to a response. |
|
83 * |
|
84 * Exceptions are not caught. |
|
85 * |
|
86 * @param Request $request A Request instance |
|
87 * @param integer $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST) |
|
88 * |
|
89 * @return Response A Response instance |
|
90 * |
|
91 * @throws \LogicException If one of the listener does not behave as expected |
|
92 * @throws NotFoundHttpException When controller cannot be found |
|
93 */ |
|
94 private function handleRaw(Request $request, $type = self::MASTER_REQUEST) |
|
95 { |
|
96 // request |
|
97 $event = new GetResponseEvent($this, $request, $type); |
|
98 $this->dispatcher->dispatch(KernelEvents::REQUEST, $event); |
|
99 |
|
100 if ($event->hasResponse()) { |
|
101 return $this->filterResponse($event->getResponse(), $request, $type); |
|
102 } |
|
103 |
|
104 // load controller |
|
105 if (false === $controller = $this->resolver->getController($request)) { |
|
106 throw new NotFoundHttpException(sprintf('Unable to find the controller for path "%s". Maybe you forgot to add the matching route in your routing configuration?', $request->getPathInfo())); |
|
107 } |
|
108 |
|
109 $event = new FilterControllerEvent($this, $controller, $request, $type); |
|
110 $this->dispatcher->dispatch(KernelEvents::CONTROLLER, $event); |
|
111 $controller = $event->getController(); |
|
112 |
|
113 // controller arguments |
|
114 $arguments = $this->resolver->getArguments($request, $controller); |
|
115 |
|
116 // call controller |
|
117 $response = call_user_func_array($controller, $arguments); |
|
118 |
|
119 // view |
|
120 if (!$response instanceof Response) { |
|
121 $event = new GetResponseForControllerResultEvent($this, $request, $type, $response); |
|
122 $this->dispatcher->dispatch(KernelEvents::VIEW, $event); |
|
123 |
|
124 if ($event->hasResponse()) { |
|
125 $response = $event->getResponse(); |
|
126 } |
|
127 |
|
128 if (!$response instanceof Response) { |
|
129 $msg = sprintf('The controller must return a response (%s given).', $this->varToString($response)); |
|
130 |
|
131 // the user may have forgotten to return something |
|
132 if (null === $response) { |
|
133 $msg .= ' Did you forget to add a return statement somewhere in your controller?'; |
|
134 } |
|
135 throw new \LogicException($msg); |
|
136 } |
|
137 } |
|
138 |
|
139 return $this->filterResponse($response, $request, $type); |
|
140 } |
|
141 |
|
142 /** |
|
143 * Filters a response object. |
|
144 * |
|
145 * @param Response $response A Response instance |
|
146 * @param Request $request A error message in case the response is not a Response object |
|
147 * @param integer $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST) |
|
148 * |
|
149 * @return Response The filtered Response instance |
|
150 * |
|
151 * @throws \RuntimeException if the passed object is not a Response instance |
|
152 */ |
|
153 private function filterResponse(Response $response, Request $request, $type) |
|
154 { |
|
155 $event = new FilterResponseEvent($this, $request, $type, $response); |
|
156 |
|
157 $this->dispatcher->dispatch(KernelEvents::RESPONSE, $event); |
|
158 |
|
159 return $event->getResponse(); |
|
160 } |
|
161 |
|
162 /** |
|
163 * Handles and exception by trying to convert it to a Response. |
|
164 * |
|
165 * @param \Exception $e An \Exception instance |
|
166 * @param Request $request A Request instance |
|
167 * @param integer $type The type of the request |
|
168 * |
|
169 * @return Response A Response instance |
|
170 */ |
|
171 private function handleException(\Exception $e, $request, $type) |
|
172 { |
|
173 $event = new GetResponseForExceptionEvent($this, $request, $type, $e); |
|
174 $this->dispatcher->dispatch(KernelEvents::EXCEPTION, $event); |
|
175 |
|
176 if (!$event->hasResponse()) { |
|
177 throw $e; |
|
178 } |
|
179 |
|
180 try { |
|
181 return $this->filterResponse($event->getResponse(), $request, $type); |
|
182 } catch (\Exception $e) { |
|
183 return $event->getResponse(); |
|
184 } |
|
185 } |
|
186 |
|
187 private function varToString($var) |
|
188 { |
|
189 if (is_object($var)) { |
|
190 return sprintf('Object(%s)', get_class($var)); |
|
191 } |
|
192 |
|
193 if (is_array($var)) { |
|
194 $a = array(); |
|
195 foreach ($var as $k => $v) { |
|
196 $a[] = sprintf('%s => %s', $k, $this->varToString($v)); |
|
197 } |
|
198 |
|
199 return sprintf("Array(%s)", implode(', ', $a)); |
|
200 } |
|
201 |
|
202 if (is_resource($var)) { |
|
203 return sprintf('Resource(%s)', get_resource_type($var)); |
|
204 } |
|
205 |
|
206 if (null === $var) { |
|
207 return 'null'; |
|
208 } |
|
209 |
|
210 if (false === $var) { |
|
211 return 'false'; |
|
212 } |
|
213 |
|
214 if (true === $var) { |
|
215 return 'true'; |
|
216 } |
|
217 |
|
218 return (string) $var; |
|
219 } |
|
220 } |