Projekt

Obecné

Profil

Stáhnout (9.86 KB) Statistiky
| Větev: | Revize:
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\BadRequestHttpException;
16
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
17
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
18
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
19
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
20
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
21
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
22
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
23
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
24
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
25
use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException;
26
use Symfony\Component\HttpFoundation\Request;
27
use Symfony\Component\HttpFoundation\RequestStack;
28
use Symfony\Component\HttpFoundation\Response;
29
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
30

    
31
/**
32
 * HttpKernel notifies events to convert a Request object to a Response one.
33
 *
34
 * @author Fabien Potencier <fabien@symfony.com>
35
 */
36
class HttpKernel implements HttpKernelInterface, TerminableInterface
37
{
38
    protected $dispatcher;
39
    protected $resolver;
40
    protected $requestStack;
41

    
42
    /**
43
     * Constructor.
44
     *
45
     * @param EventDispatcherInterface    $dispatcher   An EventDispatcherInterface instance
46
     * @param ControllerResolverInterface $resolver     A ControllerResolverInterface instance
47
     * @param RequestStack                $requestStack A stack for master/sub requests
48
     */
49
    public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver, RequestStack $requestStack = null)
50
    {
51
        $this->dispatcher = $dispatcher;
52
        $this->resolver = $resolver;
53
        $this->requestStack = $requestStack ?: new RequestStack();
54
    }
55

    
56
    /**
57
     * {@inheritdoc}
58
     */
59
    public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
60
    {
61
        $request->headers->set('X-Php-Ob-Level', ob_get_level());
62

    
63
        try {
64
            return $this->handleRaw($request, $type);
65
        } catch (\Exception $e) {
66
            if ($e instanceof ConflictingHeadersException) {
67
                $e = new BadRequestHttpException('The request headers contain conflicting information regarding the origin of this request.', $e);
68
            }
69
            if (false === $catch) {
70
                $this->finishRequest($request, $type);
71

    
72
                throw $e;
73
            }
74

    
75
            return $this->handleException($e, $request, $type);
76
        }
77
    }
78

    
79
    /**
80
     * {@inheritdoc}
81
     */
82
    public function terminate(Request $request, Response $response)
83
    {
84
        $this->dispatcher->dispatch(KernelEvents::TERMINATE, new PostResponseEvent($this, $request, $response));
85
    }
86

    
87
    /**
88
     * @throws \LogicException If the request stack is empty
89
     *
90
     * @internal
91
     */
92
    public function terminateWithException(\Exception $exception)
93
    {
94
        if (!$request = $this->requestStack->getMasterRequest()) {
95
            throw new \LogicException('Request stack is empty', 0, $exception);
96
        }
97

    
98
        $response = $this->handleException($exception, $request, self::MASTER_REQUEST);
99

    
100
        $response->sendHeaders();
101
        $response->sendContent();
102

    
103
        $this->terminate($request, $response);
104
    }
105

    
106
    /**
107
     * Handles a request to convert it to a response.
108
     *
109
     * Exceptions are not caught.
110
     *
111
     * @param Request $request A Request instance
112
     * @param int     $type    The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST)
113
     *
114
     * @return Response A Response instance
115
     *
116
     * @throws \LogicException       If one of the listener does not behave as expected
117
     * @throws NotFoundHttpException When controller cannot be found
118
     */
119
    private function handleRaw(Request $request, $type = self::MASTER_REQUEST)
120
    {
121
        $this->requestStack->push($request);
122

    
123
        // request
124
        $event = new GetResponseEvent($this, $request, $type);
125
        $this->dispatcher->dispatch(KernelEvents::REQUEST, $event);
126

    
127
        if ($event->hasResponse()) {
128
            return $this->filterResponse($event->getResponse(), $request, $type);
129
        }
130

    
131
        // load controller
132
        if (false === $controller = $this->resolver->getController($request)) {
133
            throw new NotFoundHttpException(sprintf('Unable to find the controller for path "%s". The route is wrongly configured.', $request->getPathInfo()));
134
        }
135

    
136
        $event = new FilterControllerEvent($this, $controller, $request, $type);
137
        $this->dispatcher->dispatch(KernelEvents::CONTROLLER, $event);
138
        $controller = $event->getController();
139

    
140
        // controller arguments
141
        $arguments = $this->resolver->getArguments($request, $controller);
142

    
143
        // call controller
144
        $response = call_user_func_array($controller, $arguments);
145

    
146
        // view
147
        if (!$response instanceof Response) {
148
            $event = new GetResponseForControllerResultEvent($this, $request, $type, $response);
149
            $this->dispatcher->dispatch(KernelEvents::VIEW, $event);
150

    
151
            if ($event->hasResponse()) {
152
                $response = $event->getResponse();
153
            }
154

    
155
            if (!$response instanceof Response) {
156
                $msg = sprintf('The controller must return a response (%s given).', $this->varToString($response));
157

    
158
                // the user may have forgotten to return something
159
                if (null === $response) {
160
                    $msg .= ' Did you forget to add a return statement somewhere in your controller?';
161
                }
162
                throw new \LogicException($msg);
163
            }
164
        }
165

    
166
        return $this->filterResponse($response, $request, $type);
167
    }
168

    
169
    /**
170
     * Filters a response object.
171
     *
172
     * @param Response $response A Response instance
173
     * @param Request  $request  An error message in case the response is not a Response object
174
     * @param int      $type     The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST)
175
     *
176
     * @return Response The filtered Response instance
177
     *
178
     * @throws \RuntimeException if the passed object is not a Response instance
179
     */
180
    private function filterResponse(Response $response, Request $request, $type)
181
    {
182
        $event = new FilterResponseEvent($this, $request, $type, $response);
183

    
184
        $this->dispatcher->dispatch(KernelEvents::RESPONSE, $event);
185

    
186
        $this->finishRequest($request, $type);
187

    
188
        return $event->getResponse();
189
    }
190

    
191
    /**
192
     * Publishes the finish request event, then pop the request from the stack.
193
     *
194
     * Note that the order of the operations is important here, otherwise
195
     * operations such as {@link RequestStack::getParentRequest()} can lead to
196
     * weird results.
197
     *
198
     * @param Request $request
199
     * @param int     $type
200
     */
201
    private function finishRequest(Request $request, $type)
202
    {
203
        $this->dispatcher->dispatch(KernelEvents::FINISH_REQUEST, new FinishRequestEvent($this, $request, $type));
204
        $this->requestStack->pop();
205
    }
206

    
207
    /**
208
     * Handles an exception by trying to convert it to a Response.
209
     *
210
     * @param \Exception $e       An \Exception instance
211
     * @param Request    $request A Request instance
212
     * @param int        $type    The type of the request
213
     *
214
     * @return Response A Response instance
215
     *
216
     * @throws \Exception
217
     */
218
    private function handleException(\Exception $e, $request, $type)
219
    {
220
        $event = new GetResponseForExceptionEvent($this, $request, $type, $e);
221
        $this->dispatcher->dispatch(KernelEvents::EXCEPTION, $event);
222

    
223
        // a listener might have replaced the exception
224
        $e = $event->getException();
225

    
226
        if (!$event->hasResponse()) {
227
            $this->finishRequest($request, $type);
228

    
229
            throw $e;
230
        }
231

    
232
        $response = $event->getResponse();
233

    
234
        // the developer asked for a specific status code
235
        if ($response->headers->has('X-Status-Code')) {
236
            $response->setStatusCode($response->headers->get('X-Status-Code'));
237

    
238
            $response->headers->remove('X-Status-Code');
239
        } elseif (!$response->isClientError() && !$response->isServerError() && !$response->isRedirect()) {
240
            // ensure that we actually have an error response
241
            if ($e instanceof HttpExceptionInterface) {
242
                // keep the HTTP status code and headers
243
                $response->setStatusCode($e->getStatusCode());
244
                $response->headers->add($e->getHeaders());
245
            } else {
246
                $response->setStatusCode(500);
247
            }
248
        }
249

    
250
        try {
251
            return $this->filterResponse($response, $request, $type);
252
        } catch (\Exception $e) {
253
            return $response;
254
        }
255
    }
256

    
257
    private function varToString($var)
258
    {
259
        if (is_object($var)) {
260
            return sprintf('Object(%s)', get_class($var));
261
        }
262

    
263
        if (is_array($var)) {
264
            $a = array();
265
            foreach ($var as $k => $v) {
266
                $a[] = sprintf('%s => %s', $k, $this->varToString($v));
267
            }
268

    
269
            return sprintf('Array(%s)', implode(', ', $a));
270
        }
271

    
272
        if (is_resource($var)) {
273
            return sprintf('Resource(%s)', get_resource_type($var));
274
        }
275

    
276
        if (null === $var) {
277
            return 'null';
278
        }
279

    
280
        if (false === $var) {
281
            return 'false';
282
        }
283

    
284
        if (true === $var) {
285
            return 'true';
286
        }
287

    
288
        return (string) $var;
289
    }
290
}
(4-4/14)