Passed
Push — master ( ae3209...f101e1 )
by Daniel
01:46
created

src/Middleware/ExceptionMiddleware.php (1 issue)

Labels
Severity
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
        // Log error
54 1
        if (isset($this->logger)) {
55 1
            $this->logger->error(
0 ignored issues
show
The method error() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

55
            $this->logger->/** @scrutinizer ignore-call */ 
56
                           error(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
56 1
                sprintf(
57 1
                    '%s;Code %s;File: %s;Line: %s',
58 1
                    $exception->getMessage(),
59 1
                    $exception->getCode(),
60 1
                    $exception->getFile(),
61 1
                    $exception->getLine()
62 1
                ),
63 1
                $exception->getTrace()
64 1
            );
65
        }
66
67
        // Content negotiation
68 1
        if (str_contains($request->getHeaderLine('Accept'), 'application/json')) {
69
            $response = $response->withAddedHeader('Content-Type', 'application/json');
70
71
            // JSON
72
            return $this->renderJson($exception, $response);
73
        }
74
75
        // HTML
76 1
        return $this->renderHtml($response, $exception);
77
    }
78
79
    public function renderJson(Throwable $exception, ResponseInterface $response): ResponseInterface
80
    {
81
        $data = [
82
            'error' => [
83
                'message' => $exception->getMessage(),
84
            ],
85
        ];
86
87
        return $this->renderer->json($response, $data);
88
    }
89
90 1
    public function renderHtml(ResponseInterface $response, Throwable $exception): ResponseInterface
91
    {
92 1
        $response = $response->withAddedHeader('Content-Type', 'text/html');
93
94 1
        $message = sprintf(
95 1
            "\n<br>Error %s (%s)\n<br>Message: %s\n<br>",
96 1
            $this->html($response->getStatusCode()),
97 1
            $this->html($response->getReasonPhrase()),
98 1
            $this->html($exception->getMessage()),
99 1
        );
100
101 1
        if ($this->displayErrorDetails) {
102 1
            $message .= sprintf(
103 1
                'File: %s, Line: %s',
104 1
                $this->html($exception->getFile()),
105 1
                $this->html($exception->getLine())
106 1
            );
107
        }
108
109 1
        $response->getBody()->write($message);
110
111 1
        return $response;
112
    }
113
114 1
    private function getHttpStatusCode(Throwable $exception): int
115
    {
116 1
        $statusCode = StatusCodeInterface::STATUS_INTERNAL_SERVER_ERROR;
117
118 1
        if ($exception instanceof HttpException) {
119 1
            $statusCode = $exception->getCode();
120
        }
121
122 1
        if ($exception instanceof DomainException || $exception instanceof InvalidArgumentException) {
123
            $statusCode = StatusCodeInterface::STATUS_BAD_REQUEST;
124
        }
125
126 1
        return $statusCode;
127
    }
128
129 1
    private function html(string $text): string
130
    {
131 1
        return htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
132
    }
133
}
134