Passed
Push — master ( 30cb35...94cefe )
by Anton
02:53
created

ErrorHandlerMiddleware   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 103
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 11
eloc 41
c 3
b 0
f 0
dl 0
loc 103
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A logError() 0 10 2
A process() 0 25 6
A __construct() 0 10 1
A renderException() 0 13 2
1
<?php
2
3
/**
4
 * Spiral Framework.
5
 *
6
 * @license   MIT
7
 * @author    Anton Titov (Wolfy-J)
8
 */
9
10
declare(strict_types=1);
11
12
namespace Spiral\Http\Middleware;
13
14
use Psr\Http\Message\ResponseFactoryInterface;
15
use Psr\Http\Message\ResponseInterface as Response;
16
use Psr\Http\Message\ServerRequestInterface as Request;
17
use Psr\Http\Server\MiddlewareInterface;
18
use Psr\Http\Server\RequestHandlerInterface as Handler;
19
use Spiral\Exceptions\HtmlHandler;
20
use Spiral\Exceptions\JsonHandler;
21
use Spiral\Http\ErrorHandler\RendererInterface;
22
use Spiral\Http\Exception\ClientException;
23
use Spiral\Logger\Traits\LoggerTrait;
24
use Spiral\Router\Exception\RouterException;
25
use Spiral\Snapshots\SnapshotterInterface;
26
27
/**
28
 * Wraps Client and Routing exceptions into proper response.
29
 */
30
final class ErrorHandlerMiddleware implements MiddlewareInterface
31
{
32
    use LoggerTrait;
33
34
    /** @var ResponseFactoryInterface */
35
    private $responseFactory;
36
37
    /** @var bool */
38
    private $suppressErrors;
39
40
    /** @var RendererInterface */
41
    private $renderer;
42
43
    /** @var SnapshotterInterface|null */
44
    private $snapshots;
45
46
    /**
47
     * @param bool                      $suppressErrors
48
     * @param RendererInterface         $renderer
49
     * @param ResponseFactoryInterface  $responseFactory
50
     * @param SnapshotterInterface|null $snapshots
51
     */
52
    public function __construct(
53
        bool $suppressErrors,
54
        RendererInterface $renderer,
55
        ResponseFactoryInterface $responseFactory,
56
        SnapshotterInterface $snapshots = null
57
    ) {
58
        $this->suppressErrors = $suppressErrors;
59
        $this->renderer = $renderer;
60
        $this->responseFactory = $responseFactory;
61
        $this->snapshots = $snapshots;
62
    }
63
64
    /**
65
     * @inheritdoc
66
     *
67
     * @throws \Throwable
68
     */
69
    public function process(Request $request, Handler $handler): Response
70
    {
71
        try {
72
            return $handler->handle($request);
73
        } catch (ClientException | RouterException $e) {
74
            if ($e instanceof ClientException) {
75
                $code = $e->getCode();
76
            } else {
77
                $code = 404;
78
            }
79
        } catch (\Throwable $e) {
80
            if ($this->snapshots !== null) {
81
                $this->snapshots->register($e);
82
            }
83
84
            $code = 500;
85
86
            if (!$this->suppressErrors) {
87
                return $this->renderException($request, $e);
88
            }
89
        }
90
91
        $this->logError($request, $code, $e->getMessage());
92
93
        return $this->renderer->renderException($request, $code, $e->getMessage());
94
    }
95
96
    /**
97
     * @param Request    $request
98
     * @param \Throwable $e
99
     * @return Response
100
     *
101
     * @throws \Throwable
102
     */
103
    private function renderException(Request $request, \Throwable $e): Response
104
    {
105
        $response = $this->responseFactory->createResponse(500);
106
107
        if ($request->getHeaderLine('Accept') == 'application/json') {
108
            $response = $response->withHeader('Content-Type', 'application/json');
109
            $handler = new JsonHandler();
110
        } else {
111
            $handler = new HtmlHandler();
112
        }
113
114
        $response->getBody()->write($handler->renderException($e, HtmlHandler::VERBOSITY_VERBOSE));
115
        return $response;
116
    }
117
118
    /**
119
     * @param Request $request
120
     * @param int     $code
121
     * @param string  $message
122
     */
123
    private function logError(Request $request, int $code, string $message): void
124
    {
125
        $this->getLogger()->error(sprintf(
126
            '%s://%s%s caused the error %s (%s) by client %s.',
127
            $request->getUri()->getScheme(),
128
            $request->getUri()->getHost(),
129
            $request->getUri()->getPath(),
130
            $code,
131
            $message ?: '-not specified-',
132
            $request->getServerParams()['REMOTE_ADDR'] ?? '127.0.0.1'
133
        ));
134
    }
135
}
136