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
|
|||
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 |
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.