Passed
Push — master ( fd8409...3882b7 )
by Sergei
02:48
created

ExceptionResponder   A

Complexity

Total Complexity 8

Size/Duplication

Total Lines 68
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 8
eloc 13
c 2
b 0
f 0
dl 0
loc 68
ccs 15
cts 15
cp 1
rs 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
B process() 0 24 7
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\ErrorHandler\Middleware;
6
7
use Psr\Http\Message\ResponseFactoryInterface;
8
use Psr\Http\Message\ResponseInterface;
9
use Psr\Http\Message\ServerRequestInterface;
10
use Psr\Http\Server\MiddlewareInterface;
11
use Psr\Http\Server\RequestHandlerInterface;
12
use Throwable;
13
use Yiisoft\Injector\Injector;
14
15
use function is_int;
16
use function is_callable;
17
18
/**
19
 * `ExceptionResponder` maps certain exceptions to custom responses.
20
 */
21
final class ExceptionResponder implements MiddlewareInterface
22
{
23
    /**
24
     * The `$exceptionMap` specified as an array can be in one of the following two formats:
25
     *
26
     * - callable format: `[\LogicException::class => callable, \DomainException::class => callable, ...]`
27
     * - int format: `[\Exception::class => 404, \DomainException::class => 500, ...]`
28
     *
29
     * When an exception is thrown, the map in callable format allows to take control of the response.
30
     * Сallable must return `Psr\Http\Message\ResponseInterface`. If specified exception classes are equal,
31
     * then the first one will be processed. Below are some examples:
32
     *
33
     * ```php
34
     * $exceptionMap = [
35
     *     DomainException::class => function (\Psr\Http\Message\ResponseFactoryInterface $responseFactory) {
36
     *         return $responseFactory->createResponse(\Yiisoft\Http\Status::CREATED);
37
     *     },
38
     *     MyHttpException::class => static fn (MyHttpException $exception) => new MyResponse($exception),
39
     * ]
40
     * ```
41
     *
42
     * When an exception is thrown, the map in int format allows to send the response with set http code.
43
     * If specified exception classes are equal, then the first one will be processed. Below are some examples:
44
     *
45
     * ```php
46
     * $exceptionMap = [
47
     *     \DomainException::class => \Yiisoft\Http\Status::BAD_REQUEST,
48
     *     \InvalidArgumentException::class => \Yiisoft\Http\Status::BAD_REQUEST,
49
     *     MyNotFoundException::class => \Yiisoft\Http\Status::NOT_FOUND,
50
     * ]
51
     * ```
52
     *
53
     * @param callable[]|int[] $exceptionMap A callable that must return a `ResponseInterface` or response status code.
54
     * @param bool $checkResponseBody Whether executing `getBody()` on response needs to be done. It's useful for
55
     * catching exceptions that can be thrown in the process of body generation.
56
     */
57 5
    public function __construct(
58
        private array $exceptionMap,
59
        private ResponseFactoryInterface $responseFactory,
60
        private Injector $injector,
61
        private bool $checkResponseBody = false,
62
    ) {
63 5
    }
64
65 5
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
66
    {
67
        try {
68 5
            $response = $handler->handle($request);
69 2
            if ($this->checkResponseBody) {
70 2
                $response->getBody();
71
            }
72 4
        } catch (Throwable $t) {
73 4
            foreach ($this->exceptionMap as $exceptionType => $responseHandler) {
74 3
                if ($t instanceof $exceptionType) {
75 2
                    if (is_int($responseHandler)) {
76 1
                        return $this->responseFactory->createResponse($responseHandler);
77
                    }
78
79 1
                    if (is_callable($responseHandler)) {
80
                        /** @var ResponseInterface */
81 1
                        return $this->injector->invoke($responseHandler, ['exception' => $t]);
82
                    }
83
                }
84
            }
85 2
            throw $t;
86
        }
87
88 1
        return $response;
89
    }
90
}
91