Test Failed
Push — master ( 728572...c0d895 )
by Anatoly
05:42 queued 03:03
created

ErrorHandlingMiddleware::handleResourceNotFound()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
nc 1
nop 2
dl 0
loc 9
c 1
b 0
f 0
cc 1
rs 10
ccs 0
cts 0
cp 0
crap 2
1
<?php declare(strict_types=1);
2
3
namespace App\Middleware;
4
5
/**
6
 * Import classes
7
 */
8
use App\ContainerAwareTrait;
9
use App\Exception\InvalidEntityException;
10
use App\Exception\ResourceNotFoundException;
11
use Arus\Http\Response\ResponseFactoryAwareTrait;
12
use Psr\Http\Message\ResponseInterface;
13
use Psr\Http\Message\ServerRequestInterface;
14
use Psr\Http\Server\MiddlewareInterface;
15
use Psr\Http\Server\RequestHandlerInterface;
16
use Sunrise\Http\Router\Exception\BadRequestException;
17
use Sunrise\Http\Router\Exception\MethodNotAllowedException;
18
use Sunrise\Http\Router\Exception\RouteNotFoundException;
19
use Sunrise\Http\Router\Exception\UnsupportedMediaTypeException;
20
use Symfony\Component\Validator\ConstraintViolation;
21
use Symfony\Component\Validator\ConstraintViolationList;
22
use Whoops\Run as Whoops;
23
use Whoops\Handler\PrettyPageHandler;
24
use Throwable;
25
26
/**
27
 * Import functions
28
 */
29
use function get_class;
30
use function implode;
31
use function preg_quote;
32
use function preg_replace;
33
use function sprintf;
34
35
/**
36
 * ErrorHandlingMiddleware
37
 */
38
final class ErrorHandlingMiddleware implements MiddlewareInterface
39
{
40
    use ContainerAwareTrait;
41
    use ResponseFactoryAwareTrait;
42
43
    /**
44
     * Error codes
45
     */
46
    public const BAD_REQUEST_ERROR_CODE = 'b187c971-810b-455a-baf3-06dc6a1591f4';
47
    public const METHOD_NOT_ALLOWED_ERROR_CODE = '7d8f78d7-c689-409b-8031-8401ab5836b6';
48
    public const ROUTE_NOT_FOUND_ERROR_CODE = '979775e6-a43b-414f-bb72-cbe0133f621e';
49
    public const RESOURCE_NOT_FOUND_ERROR_CODE = 'cb378ff8-a1ec-48ba-baad-2c61f47ce95e';
50
    public const UNSUPPORTED_MEDIA_TYPE_ERROR_CODE = '87255179-5041-4f1b-a469-b891ad5dc623';
51
    public const UNCAUGHT_EXCEPTION_ERROR_CODE = '594358d2-b5f1-4cfc-8c60-df43cfd720b3';
52
53
    /**
54
     * {@inheritDoc}
55
     *
56
     * @param ServerRequestInterface $request
57
     * @param RequestHandlerInterface $handler
58
     *
59
     * @return ResponseInterface
60
     */
61
    public function process(
62
        ServerRequestInterface $request,
63
        RequestHandlerInterface $handler
64
    ) : ResponseInterface {
65
        try {
66
            return $handler->handle($request);
67
        } catch (BadRequestException $e) {
68
            return $this->handleBadRequest($request, $e);
69
        } catch (MethodNotAllowedException $e) {
70
            return $this->handleMethodNotAllowed($request, $e);
71
        } catch (RouteNotFoundException $e) {
72
            return $this->handleRouteNotFound($request, $e);
73
        } catch (ResourceNotFoundException $e) {
74
            return $this->handleResourceNotFound($request, $e);
75
        } catch (UnsupportedMediaTypeException $e) {
76
            return $this->handleUnsupportedMediaType($request, $e);
77
        } catch (InvalidEntityException $e) {
78
            return $this->handleInvalidEntity($request, $e);
79
        } catch (Throwable $e) {
80
            return $this->handleException($request, $e);
81
        }
82
    }
83
84
    /**
85
     * Returns a response with the given processed exception
86
     *
87
     * @param ServerRequestInterface $request
88
     * @param BadRequestException $exception
89
     *
90
     * @return ResponseInterface
91
     */
92
    private function handleBadRequest(
93
        ServerRequestInterface $request,
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

93
        /** @scrutinizer ignore-unused */ ServerRequestInterface $request,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
94
        BadRequestException $exception
95
    ) : ResponseInterface {
96
        $violations = new ConstraintViolationList();
97
98
        foreach ($exception->getViolations() as $v) {
99
            $violations->add(new ConstraintViolation(
100
                $v['message'],
101
                null,
102
                [],
103
                null,
104
                $v['property'],
105
                null,
106
                null,
107
                self::BAD_REQUEST_ERROR_CODE,
108
                null,
109
                null
110
            ));
111
        }
112
113
        return $this->violations($violations, 400);
114
    }
115
116
    /**
117
     * Returns a response with the given processed exception
118
     *
119
     * @param ServerRequestInterface $request
120
     * @param MethodNotAllowedException $exception
121
     *
122
     * @return ResponseInterface
123
     */
124
    private function handleMethodNotAllowed(
125
        ServerRequestInterface $request,
126
        MethodNotAllowedException $exception
127
    ) : ResponseInterface {
128
        return $this->error(
129
            $exception->getMessage(),
130
            $request->getUri()->getPath(),
131
            self::METHOD_NOT_ALLOWED_ERROR_CODE,
132
            405
133
        )->withHeader('Allow', implode(',', $exception->getAllowedMethods()));
134
    }
135
136
    /**
137
     * Returns a response with the given processed exception
138
     *
139
     * @param ServerRequestInterface $request
140
     * @param RouteNotFoundException $exception
141
     *
142
     * @return ResponseInterface
143
     */
144
    private function handleRouteNotFound(
145
        ServerRequestInterface $request,
146
        RouteNotFoundException $exception
147
    ) : ResponseInterface {
148
        return $this->error(
149
            $exception->getMessage(),
150
            $request->getUri()->getPath(),
151
            self::ROUTE_NOT_FOUND_ERROR_CODE,
152
            404
153
        );
154
    }
155
156
    /**
157
     * Returns a response with the given processed exception
158
     *
159
     * @param ServerRequestInterface $request
160
     * @param ResourceNotFoundException $exception
161
     *
162
     * @return ResponseInterface
163
     */
164
    private function handleResourceNotFound(
165
        ServerRequestInterface $request,
166
        ResourceNotFoundException $exception
167
    ) : ResponseInterface {
168
        return $this->error(
169
            $exception->getMessage(),
170
            $request->getUri()->getPath(),
171
            self::RESOURCE_NOT_FOUND_ERROR_CODE,
172
            404
173
        );
174
    }
175
176
    /**
177
     * Returns a response with the given processed exception
178
     *
179
     * @param ServerRequestInterface $request
180
     * @param UnsupportedMediaTypeException $exception
181
     *
182
     * @return ResponseInterface
183
     */
184
    private function handleUnsupportedMediaType(
185
        ServerRequestInterface $request,
186
        UnsupportedMediaTypeException $exception
187
    ) : ResponseInterface {
188
        return $this->error(
189
            $exception->getMessage(),
190
            $request->getUri()->getPath(),
191
            self::UNSUPPORTED_MEDIA_TYPE_ERROR_CODE,
192
            415
193
        )->withHeader('Accept', implode(',', $exception->getSupportedTypes()));
194
    }
195
196
    /**
197
     * Returns a response with the given processed exception
198
     *
199
     * @param ServerRequestInterface $request
200
     * @param InvalidEntityException $exception
201
     *
202
     * @return ResponseInterface
203
     */
204
    private function handleInvalidEntity(
205
        ServerRequestInterface $request,
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

205
        /** @scrutinizer ignore-unused */ ServerRequestInterface $request,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
206
        InvalidEntityException $exception
207
    ) : ResponseInterface {
208
        return $this->violations($exception->getEntityViolations(), 400);
209
    }
210
211
    /**
212
     * Returns a response with the given processed exception
213
     *
214
     * @param ServerRequestInterface $request
215
     * @param Throwable $exception
216
     *
217
     * @return ResponseInterface
218
     *
219
     * @link https://github.com/filp/whoops
220
     */
221
    private function handleException(
222
        ServerRequestInterface $request,
223
        Throwable $exception
224
    ) : ResponseInterface {
225
        $this->container->get('logger')->error($exception->getMessage(), [
226
            'exception' => $exception,
227
        ]);
228
229
        if (!$this->container->get('app.display_errors')) {
230
            return $this->error(
231
                sprintf(
232
                    'Caught the exception %s in the file %s on line %d.',
233
                    get_class($exception),
234
                    $this->hideRoot($exception->getFile()),
235
                    $exception->getLine()
236
                ),
237
                $request->getUri()->getPath(),
238
                self::UNCAUGHT_EXCEPTION_ERROR_CODE,
239
                500
240
            );
241
        }
242
243
        $whoops = new Whoops();
244
        $whoops->allowQuit(false);
245
        $whoops->sendHttpCode(false);
246
        $whoops->writeToOutput(false);
247
        $whoops->pushHandler(new PrettyPageHandler());
248
249
        return $this->html($whoops->handleException($exception), 500);
250
    }
251
252
    /**
253
     * Hides the application root from the given path
254
     *
255
     * @param string $path
256
     *
257
     * @return string
258
     */
259
    private function hideRoot(string $path) : string
260
    {
261
        $root = preg_quote($this->container->get('app.root'), '/');
262
263
        $path = preg_replace('/^' . $root . '/ui', '', $path);
264
265
        return $path;
266
    }
267
}
268