Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
40 / 40 |
|
100.00% |
4 / 4 |
CRAP | |
100.00% |
1 / 1 |
ErrorHandler | |
100.00% |
40 / 40 |
|
100.00% |
4 / 4 |
16 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
process | |
100.00% |
32 / 32 |
|
100.00% |
1 / 1 |
10 | |||
selectView | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
createResponse | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
redirectBack | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace Engelsystem\Middleware; |
6 | |
7 | use Engelsystem\Controllers\NotificationType; |
8 | use Engelsystem\Http\Exceptions\HttpException; |
9 | use Engelsystem\Http\Exceptions\ValidationException; |
10 | use Engelsystem\Http\Request; |
11 | use Engelsystem\Http\Response; |
12 | use Illuminate\Database\Eloquent\ModelNotFoundException; |
13 | use Illuminate\Support\Arr; |
14 | use Psr\Http\Message\ResponseInterface; |
15 | use Psr\Http\Message\ServerRequestInterface; |
16 | use Psr\Http\Server\MiddlewareInterface; |
17 | use Psr\Http\Server\RequestHandlerInterface; |
18 | use Twig\Loader\LoaderInterface as TwigLoader; |
19 | |
20 | class ErrorHandler implements MiddlewareInterface |
21 | { |
22 | protected string $viewPrefix = 'errors/'; |
23 | |
24 | /** |
25 | * A list of inputs that are not saved from input |
26 | * |
27 | * @var array<string> |
28 | */ |
29 | protected array $formIgnore = [ |
30 | 'password', |
31 | 'password_confirmation', |
32 | 'password2', |
33 | 'new_password', |
34 | 'new_password2', |
35 | 'new_pw', |
36 | 'new_pw2', |
37 | '_token', |
38 | ]; |
39 | |
40 | public function __construct(protected TwigLoader $loader) |
41 | { |
42 | } |
43 | |
44 | /** |
45 | * Handles any error messages / http exceptions / validation errors |
46 | * |
47 | * Should be added at the beginning |
48 | */ |
49 | public function process( |
50 | ServerRequestInterface $request, |
51 | RequestHandlerInterface $handler |
52 | ): ResponseInterface { |
53 | // Handle response |
54 | try { |
55 | $response = $handler->handle($request); |
56 | } catch (HttpException $e) { |
57 | $response = $this->createResponse($e->getMessage(), $e->getStatusCode(), $e->getHeaders()); |
58 | } catch (ValidationException $e) { |
59 | $response = $this->redirectBack(); |
60 | $response->with( |
61 | 'messages.' . NotificationType::ERROR->value, |
62 | ['validation' => $e->getValidator()->getErrors()] |
63 | ); |
64 | |
65 | if ($request instanceof Request) { |
66 | $response->withInput(Arr::except($request->request->all(), $this->formIgnore)); |
67 | } |
68 | } catch (ModelNotFoundException) { |
69 | $response = $this->createResponse('', 404); |
70 | } |
71 | |
72 | $statusCode = $response->getStatusCode(); |
73 | $contentType = $response->getHeader('content-type'); |
74 | $contentType = array_shift($contentType); |
75 | if (!$contentType && str_contains($response->getBody()?->getContents() ?? '', '<html')) { |
76 | $contentType = 'text/html'; |
77 | } |
78 | |
79 | // Handle response based on status |
80 | if ( |
81 | $statusCode < 400 |
82 | || !$response instanceof Response |
83 | || !empty($contentType) |
84 | ) { |
85 | return $response; |
86 | } |
87 | |
88 | $view = $this->selectView($statusCode); |
89 | |
90 | return $response->withView( |
91 | $this->viewPrefix . $view, |
92 | [ |
93 | 'status' => $statusCode, |
94 | 'content' => $response->getContent(), |
95 | ], |
96 | $statusCode, |
97 | $response->getHeaders() |
98 | ); |
99 | } |
100 | |
101 | /** |
102 | * Select a view based on the given status code |
103 | */ |
104 | protected function selectView(int $statusCode): string |
105 | { |
106 | $hundreds = intdiv($statusCode, 100); |
107 | |
108 | $viewsList = [$statusCode, $hundreds, $hundreds * 100]; |
109 | foreach ($viewsList as $view) { |
110 | if ($this->loader->exists($this->viewPrefix . $view)) { |
111 | return (string) $view; |
112 | } |
113 | } |
114 | |
115 | return 'default'; |
116 | } |
117 | |
118 | /** |
119 | * Create a new response |
120 | * |
121 | * @codeCoverageIgnore |
122 | */ |
123 | protected function createResponse(string $content = '', int $status = 200, array $headers = []): ResponseInterface |
124 | { |
125 | return response($content, $status, $headers); |
126 | } |
127 | |
128 | /** |
129 | * Create a redirect back response |
130 | */ |
131 | protected function redirectBack(): Response |
132 | { |
133 | return back(); |
134 | } |
135 | } |