147e09013dd93fbfd7a1753797c43f921685e247
[yaffs-website] / http-kernel / EventListener / ExceptionListener.php
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\EventListener;
13
14 use Psr\Log\LoggerInterface;
15 use Symfony\Component\Debug\Exception\FlattenException;
16 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
17 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
18 use Symfony\Component\HttpFoundation\Request;
19 use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
20 use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
21 use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
22 use Symfony\Component\HttpKernel\HttpKernelInterface;
23 use Symfony\Component\HttpKernel\KernelEvents;
24 use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
25
26 /**
27  * ExceptionListener.
28  *
29  * @author Fabien Potencier <fabien@symfony.com>
30  */
31 class ExceptionListener implements EventSubscriberInterface
32 {
33     protected $controller;
34     protected $logger;
35     protected $debug;
36
37     public function __construct($controller, LoggerInterface $logger = null, $debug = false)
38     {
39         $this->controller = $controller;
40         $this->logger = $logger;
41         $this->debug = $debug;
42     }
43
44     public function onKernelException(GetResponseForExceptionEvent $event)
45     {
46         $exception = $event->getException();
47         $request = $event->getRequest();
48         $eventDispatcher = \func_num_args() > 2 ? func_get_arg(2) : null;
49
50         $this->logException($exception, sprintf('Uncaught PHP Exception %s: "%s" at %s line %s', \get_class($exception), $exception->getMessage(), $exception->getFile(), $exception->getLine()));
51
52         $request = $this->duplicateRequest($exception, $request);
53
54         try {
55             $response = $event->getKernel()->handle($request, HttpKernelInterface::SUB_REQUEST, false);
56         } catch (\Exception $e) {
57             $this->logException($e, sprintf('Exception thrown when handling an exception (%s: %s at %s line %s)', \get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()));
58
59             $wrapper = $e;
60
61             while ($prev = $wrapper->getPrevious()) {
62                 if ($exception === $wrapper = $prev) {
63                     throw $e;
64                 }
65             }
66
67             $prev = new \ReflectionProperty($wrapper instanceof \Exception ? \Exception::class : \Error::class, 'previous');
68             $prev->setAccessible(true);
69             $prev->setValue($wrapper, $exception);
70
71             throw $e;
72         }
73
74         $event->setResponse($response);
75
76         if ($this->debug && $eventDispatcher instanceof EventDispatcherInterface) {
77             $cspRemovalListener = function (FilterResponseEvent $event) use (&$cspRemovalListener, $eventDispatcher) {
78                 $event->getResponse()->headers->remove('Content-Security-Policy');
79                 $eventDispatcher->removeListener(KernelEvents::RESPONSE, $cspRemovalListener);
80             };
81             $eventDispatcher->addListener(KernelEvents::RESPONSE, $cspRemovalListener, -128);
82         }
83     }
84
85     public static function getSubscribedEvents()
86     {
87         return array(
88             KernelEvents::EXCEPTION => array('onKernelException', -128),
89         );
90     }
91
92     /**
93      * Logs an exception.
94      *
95      * @param \Exception $exception The \Exception instance
96      * @param string     $message   The error message to log
97      */
98     protected function logException(\Exception $exception, $message)
99     {
100         if (null !== $this->logger) {
101             if (!$exception instanceof HttpExceptionInterface || $exception->getStatusCode() >= 500) {
102                 $this->logger->critical($message, array('exception' => $exception));
103             } else {
104                 $this->logger->error($message, array('exception' => $exception));
105             }
106         }
107     }
108
109     /**
110      * Clones the request for the exception.
111      *
112      * @param \Exception $exception The thrown exception
113      * @param Request    $request   The original request
114      *
115      * @return Request $request The cloned request
116      */
117     protected function duplicateRequest(\Exception $exception, Request $request)
118     {
119         $attributes = array(
120             '_controller' => $this->controller,
121             'exception' => FlattenException::create($exception),
122             'logger' => $this->logger instanceof DebugLoggerInterface ? $this->logger : null,
123         );
124         $request = $request->duplicate(null, null, $attributes);
125         $request->setMethod('GET');
126
127         return $request;
128     }
129 }