Passed
Push — master ( af617e...ae3209 )
by Daniel
01:48
created

ExceptionMiddleware::process()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 2
rs 10
1
<?php
2
3
namespace App\Middleware;
4
5
use App\Renderer\JsonRenderer;
6
use DomainException;
7
use Fig\Http\Message\StatusCodeInterface;
8
use InvalidArgumentException;
9
use Psr\Http\Message\ResponseFactoryInterface;
10
use Psr\Http\Message\ResponseInterface;
11
use Psr\Http\Message\ServerRequestInterface;
12
use Psr\Http\Server\MiddlewareInterface;
13
use Psr\Http\Server\RequestHandlerInterface;
14
use Psr\Log\LoggerInterface;
15
use Slim\Exception\HttpException;
16
use Throwable;
17
18
final class ExceptionMiddleware implements MiddlewareInterface
19
{
20
    private ResponseFactoryInterface $responseFactory;
21
    private JsonRenderer $renderer;
22
    private ?LoggerInterface $logger;
23
    private bool $displayErrorDetails;
24
25 2
    public function __construct(
26
        ResponseFactoryInterface $responseFactory,
27
        JsonRenderer $jsonRenderer,
28
        LoggerInterface $logger = null,
29
        bool $displayErrorDetails = false,
30
    ) {
31 2
        $this->responseFactory = $responseFactory;
32 2
        $this->renderer = $jsonRenderer;
33 2
        $this->displayErrorDetails = $displayErrorDetails;
34 2
        $this->logger = $logger;
35
    }
36
37 2
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
38
    {
39
        try {
40 2
            return $handler->handle($request);
41 1
        } catch (Throwable $exception) {
42 1
            return $this->render($exception, $request);
43
        }
44
    }
45
46 1
    private function render(
47
        Throwable $exception,
48
        ServerRequestInterface $request,
49
    ): ResponseInterface {
50 1
        $httpStatusCode = $this->getHttpStatusCode($exception);
51 1
        $response = $this->responseFactory->createResponse($httpStatusCode);
52
53
        // Content negotiation
54 1
        if (str_contains($request->getHeaderLine('Accept'), 'application/json')) {
55
            $response = $response->withAddedHeader('Content-Type', 'application/json');
56
57
            // JSON
58
            return $this->renderJson($exception, $response);
59
        }
60
61
        // HTML
62 1
        return $this->renderHtml($response, $exception);
63
    }
64
65
    public function renderJson(Throwable $exception, ResponseInterface $response): ResponseInterface
66
    {
67
        $data = [
68
            'error' => [
69
                'message' => $exception->getMessage(),
70
            ],
71
        ];
72
73
        return $this->renderer->json($response, $data);
74
    }
75
76 1
    public function renderHtml(ResponseInterface $response, Throwable $exception): ResponseInterface
77
    {
78 1
        $response = $response->withAddedHeader('Content-Type', 'text/html');
79
80 1
        $message = sprintf(
81 1
            "\n<br>Error %s (%s)\n<br>Message: %s\n<br>",
82 1
            $this->html($response->getStatusCode()),
83 1
            $this->html($response->getReasonPhrase()),
84 1
            $this->html($exception->getMessage()),
85 1
        );
86
87 1
        if ($this->displayErrorDetails) {
88 1
            $message .= sprintf(
89 1
                'File: %s, Line: %s',
90 1
                $this->html($exception->getFile()),
91 1
                $this->html($exception->getLine())
92 1
            );
93
        }
94
95 1
        $response->getBody()->write($message);
96
97 1
        return $response;
98
    }
99
100 1
    private function getHttpStatusCode(Throwable $exception): int
101
    {
102 1
        $statusCode = StatusCodeInterface::STATUS_INTERNAL_SERVER_ERROR;
103
104 1
        if ($exception instanceof HttpException) {
105 1
            $statusCode = $exception->getCode();
106
        }
107
108 1
        if ($exception instanceof DomainException || $exception instanceof InvalidArgumentException) {
109
            $statusCode = StatusCodeInterface::STATUS_BAD_REQUEST;
110
        }
111
112 1
        return $statusCode;
113
    }
114
115 1
    private function html(string $text): string
116
    {
117 1
        return htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
118
    }
119
}
120