ErrorHandlingMiddleware::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 0
nc 1
nop 12
dl 0
loc 17
ccs 1
cts 1
cp 1
crap 1
rs 10
c 2
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * It's free open-source software released under the MIT License.
5
 *
6
 * @author Anatoly Nekhay <[email protected]>
7
 * @copyright Copyright (c) 2018, Anatoly Nekhay
8
 * @license https://github.com/sunrise-php/http-router/blob/master/LICENSE
9
 * @link https://github.com/sunrise-php/http-router
10
 */
11
12
declare(strict_types=1);
13
14
namespace Sunrise\Http\Router\Middleware;
15
16
use Psr\Http\Message\ResponseFactoryInterface;
17
use Psr\Http\Message\ResponseInterface;
18
use Psr\Http\Message\ServerRequestInterface;
19
use Psr\Http\Message\StreamFactoryInterface;
20
use Psr\Http\Server\MiddlewareInterface;
21
use Psr\Http\Server\RequestHandlerInterface;
22
use Psr\Log\LoggerInterface;
23
use Sunrise\Coder\CodecManagerInterface;
24
use Sunrise\Coder\MediaTypeInterface;
25
use Sunrise\Http\Router\Dictionary\HeaderName;
26
use Sunrise\Http\Router\Exception\HttpException;
27
use Sunrise\Http\Router\Exception\HttpExceptionFactory;
28
use Sunrise\Http\Router\LanguageInterface;
29
use Sunrise\Http\Router\ServerRequest;
30
use Sunrise\Http\Router\Validation\ConstraintViolationInterface;
31
use Sunrise\Http\Router\View\ErrorView;
32
use Sunrise\Http\Router\View\ViolationView;
33
use Sunrise\Translator\TranslatorManagerInterface;
34
use Throwable;
35
36
use function sprintf;
37
38
final class ErrorHandlingMiddleware implements MiddlewareInterface
39
{
40 6
    public function __construct(
41
        private readonly ResponseFactoryInterface $responseFactory,
42
        private readonly StreamFactoryInterface $streamFactory,
43
        private readonly CodecManagerInterface $codecManager,
44
        /** @var array<array-key, mixed> */
45
        private readonly array $codecContext,
46
        /** @var list<MediaTypeInterface> */
47
        private readonly array $producedMediaTypes,
48
        private readonly MediaTypeInterface $defaultMediaType,
49
        private readonly TranslatorManagerInterface $translatorManager,
50
        /** @var list<LanguageInterface> */
51
        private readonly array $producedLanguages,
52
        private readonly LanguageInterface $defaultLanguage,
53
        private readonly LoggerInterface $logger,
54
        private readonly ?int $fatalErrorStatusCode = null,
55
        private readonly ?string $fatalErrorMessage = null,
56
    ) {
57 6
    }
58
59
    /**
60
     * @inheritDoc
61
     */
62 6
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
63
    {
64
        try {
65 6
            return $handler->handle($request);
66 5
        } catch (HttpException $e) {
67 1
            return $this->handleHttpError($e, $request);
68 4
        } catch (Throwable $e) {
69 4
            return $this->handleFatalError($e, $request);
70
        }
71
    }
72
73 5
    private function handleHttpError(HttpException $error, ServerRequestInterface $request): ResponseInterface
74
    {
75 5
        $clientPreferredMediaType = ServerRequest::create($request)
76 5
            ->getClientPreferredMediaType(...$this->producedMediaTypes)
77 5
                ?? $this->defaultMediaType;
78
79 5
        $clientPreferredLanguage = ServerRequest::create($request)
80 5
            ->getClientPreferredLanguage(...$this->producedLanguages)
81 5
                ?? $this->defaultLanguage;
82
83 5
        return $this->createErrorResponse($error, $clientPreferredMediaType, $clientPreferredLanguage);
84
    }
85
86 4
    private function handleFatalError(Throwable $error, ServerRequestInterface $request): ResponseInterface
87
    {
88 4
        $this->logger->error($error->getMessage(), [
89 4
            'error' => $error,
90 4
            'request' => $request,
91 4
        ]);
92
93 4
        $httpError = HttpExceptionFactory::internalServerError(
94 4
            message: $this->fatalErrorMessage,
95 4
            code: $this->fatalErrorStatusCode,
96 4
            previous: $error,
97 4
        );
98
99 4
        return $this->handleHttpError($httpError, $request);
100
    }
101
102 5
    private function createErrorResponse(
103
        HttpException $error,
104
        MediaTypeInterface $mediaType,
105
        LanguageInterface $language,
106
    ): ResponseInterface {
107 5
        $response = $this->responseFactory->createResponse($error->getCode());
108 5
        foreach ($error->getHeaderFields() as [$fieldName, $fieldValue]) {
109 1
            $response = $response->withHeader($fieldName, $fieldValue);
110
        }
111
112 5
        $errorView = $this->createErrorView($error, $language);
113 5
        $responseContent = $this->codecManager->encode($mediaType, $errorView, $this->codecContext);
114 5
        $responseContentType = sprintf('%s; charset=UTF-8', $mediaType->getIdentifier());
115
116 5
        return $response->withHeader(HeaderName::CONTENT_TYPE, $responseContentType)
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response->withHe...ream($responseContent)) returns the type Psr\Http\Message\MessageInterface which includes types incompatible with the type-hinted return Psr\Http\Message\ResponseInterface.
Loading history...
117 5
            ->withBody($this->streamFactory->createStream($responseContent));
118
    }
119
120 5
    private function createErrorView(
121
        HttpException $error,
122
        LanguageInterface $language,
123
    ): ErrorView {
124 5
        $message = $this->translatorManager->translate(
125 5
            domain: $error->getTranslationDomain(),
126 5
            locale: $language->getCode(),
127 5
            template: $error->getMessageTemplate(),
128 5
            placeholders: $error->getMessagePlaceholders(),
129 5
        );
130
131 5
        $violationViews = [];
132 5
        foreach ($error->getConstraintViolations() as $violation) {
133 1
            $violationViews[] = $this->createViolationView($violation, $language);
134
        }
135
136 5
        return new ErrorView(
137 5
            message: $message,
138 5
            violations: $violationViews,
139 5
        );
140
    }
141
142 1
    private function createViolationView(
143
        ConstraintViolationInterface $violation,
144
        LanguageInterface $language,
145
    ): ViolationView {
146 1
        $message = $this->translatorManager->translate(
147 1
            domain: $violation->getTranslationDomain(),
148 1
            locale: $language->getCode(),
149 1
            template: $violation->getMessageTemplate(),
150 1
            placeholders: $violation->getMessagePlaceholders(),
151 1
        );
152
153 1
        return new ViolationView(
154 1
            source: $violation->getPropertyPath(),
155 1
            message: $message,
156 1
            code: $violation->getCode(),
157 1
        );
158
    }
159
}
160