Test Failed
Push — master ( 17aae5...cb6d8d )
by Daniel
02:30 queued 39s
created

ExceptionMiddleware::renderHtml()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 13
c 2
b 0
f 0
nc 2
nop 2
dl 0
loc 22
ccs 0
cts 16
cp 0
crap 6
rs 9.8333
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;
0 ignored issues
show
Bug introduced by
The type Slim\Exception\HttpException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
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
    public function __construct(
26
        ResponseFactoryInterface $responseFactory,
27
        JsonRenderer $jsonRenderer,
28
        LoggerInterface $logger = null,
29
        bool $displayErrorDetails = false,
30
    ) {
31
        $this->responseFactory = $responseFactory;
32
        $this->renderer = $jsonRenderer;
33
        $this->displayErrorDetails = $displayErrorDetails;
34
        $this->logger = $logger;
35
    }
36
37
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
38
    {
39
        try {
40
            return $handler->handle($request);
41
        } catch (Throwable $exception) {
42
            return $this->render($exception, $request);
43
        }
44
    }
45
46
    private function render(
47
        Throwable $exception,
48
        ServerRequestInterface $request,
49
    ): ResponseInterface {
50
        $httpStatusCode = $this->getHttpStatusCode($exception);
51
        $response = $this->responseFactory->createResponse($httpStatusCode);
52
53
        // Log error
54
        if (isset($this->logger)) {
55
            $this->logger->error(
0 ignored issues
show
Bug introduced by
The method error() does not exist on null. ( Ignorable by Annotation )

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

55
            $this->logger->/** @scrutinizer ignore-call */ 
56
                           error(

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.

Loading history...
56
                sprintf(
57
                    '%s;Code %s;File: %s;Line: %s',
58
                    $exception->getMessage(),
59
                    $exception->getCode(),
60
                    $exception->getFile(),
61
                    $exception->getLine()
62
                ),
63
                $exception->getTrace()
64
            );
65
        }
66
67
        // Content negotiation
68
        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
        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
    public function renderHtml(ResponseInterface $response, Throwable $exception): ResponseInterface
91
    {
92
        $response = $response->withHeader('Content-Type', 'text/html');
93
94
        $message = sprintf(
95
            "\n<br>Error %s (%s)\n<br>Message: %s\n<br>",
96
            $this->html((string)$response->getStatusCode()),
0 ignored issues
show
Bug introduced by
The method getStatusCode() does not exist on Psr\Http\Message\MessageInterface. It seems like you code against a sub-type of Psr\Http\Message\MessageInterface such as Psr\Http\Message\ResponseInterface. ( Ignorable by Annotation )

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

96
            $this->html((string)$response->/** @scrutinizer ignore-call */ getStatusCode()),
Loading history...
97
            $this->html($response->getReasonPhrase()),
0 ignored issues
show
Bug introduced by
The method getReasonPhrase() does not exist on Psr\Http\Message\MessageInterface. It seems like you code against a sub-type of Psr\Http\Message\MessageInterface such as Psr\Http\Message\ResponseInterface. ( Ignorable by Annotation )

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

97
            $this->html($response->/** @scrutinizer ignore-call */ getReasonPhrase()),
Loading history...
98
            $this->html($exception->getMessage()),
99
        );
100
101
        if ($this->displayErrorDetails) {
102
            $message .= sprintf(
103
                'File: %s, Line: %s',
104
                $this->html($exception->getFile()),
105
                $this->html((string)$exception->getLine())
106
            );
107
        }
108
109
        $response->getBody()->write($message);
110
111
        return $response;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response returns the type Psr\Http\Message\MessageInterface which includes types incompatible with the type-hinted return Psr\Http\Message\ResponseInterface.
Loading history...
112
    }
113
114
    private function getHttpStatusCode(Throwable $exception): int
115
    {
116
        $statusCode = StatusCodeInterface::STATUS_INTERNAL_SERVER_ERROR;
117
118
        if ($exception instanceof HttpException) {
119
            $statusCode = $exception->getCode();
120
        }
121
122
        if ($exception instanceof DomainException || $exception instanceof InvalidArgumentException) {
123
            $statusCode = StatusCodeInterface::STATUS_BAD_REQUEST;
124
        }
125
126
        return $statusCode;
127
    }
128
129
    private function html(string $text): string
130
    {
131
        return htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
132
    }
133
}
134