ExceptionHandler::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 7
cts 7
cp 1
rs 9.8333
c 0
b 0
f 0
cc 1
nc 1
nop 5
crap 1
1
<?php
2
3
namespace Equip\Handler;
4
5
use Equip\Exception\HttpException;
6
use Exception;
7
use InvalidArgumentException;
8
use Negotiation\Negotiator;
9
use Psr\Http\Message\ResponseInterface;
10
use Psr\Http\Message\ServerRequestInterface;
11
use Psr\Log\LoggerInterface;
12
use Relay\ResolverInterface;
13
use Whoops\Run as Whoops;
14
15
class ExceptionHandler
16
{
17
    /**
18
     * @var Negotiator
19
     */
20
    private $negotiator;
21
22
    /**
23
     * @var ExceptionHandlerPreferences
24
     */
25
    private $preferences;
26
27
    /**
28
     * @var ResolverInterface
29
     */
30
    private $resolver;
31
32
    /**
33
     * @var Whoops
34
     */
35
    private $whoops;
36
37
    /**
38
     * @var LoggerInterface|null
39
     */
40
    private $logger;
41
42
    /**
43
     * @param ExceptionHandlerPreferences $preferences
44
     * @param Negotiator $negotiator
45
     * @param ResolverInterface $resolver
46
     * @param Whoops $whoops
47
     * @param LoggerInterface|null $logger
48
     */
49 13
    public function __construct(
50
        ExceptionHandlerPreferences $preferences,
51
        Negotiator $negotiator,
52
        ResolverInterface $resolver,
53
        Whoops $whoops,
54
        LoggerInterface $logger = null
55
    ) {
56 13
        $this->preferences = $preferences;
57 13
        $this->logger = $logger;
58 13
        $this->negotiator = $negotiator;
59 13
        $this->resolver = $resolver;
60 13
        $this->whoops = $whoops;
61 13
    }
62
63
    /**
64
     * @param ServerRequestInterface $request
65
     * @param ResponseInterface $response
66
     * @param callable $next
67
     *
68
     * @return ResponseInterface
69
     */
70 13
    public function __invoke(
71
        ServerRequestInterface $request,
72
        ResponseInterface $response,
73
        callable $next
74
    ) {
75
        try {
76 13
            return $next($request, $response);
77 13
        } catch (Exception $e) {
78 13
            if ($this->logger) {
79 13
                if ($e instanceof HttpException) {
80 2
                    $this->logger->debug($e->getMessage(), ['exception' => $e]);
81 2
                } else {
82 11
                    $this->logger->error($e->getMessage(), ['exception' => $e]);
83
                }
84 13
            }
85
86 13
            $type = $this->type($request);
87
88 13
            $response = $response->withHeader('Content-Type', $type);
89
90
            try {
91 13
                if (method_exists($e, 'getHttpStatus')) {
92 1
                    $code = $e->getHttpStatus();
93 1
                } else {
94 12
                    $code = $e->getCode();
95
                }
96 13
                $response = $response->withStatus($code);
97 13
            } catch (InvalidArgumentException $_) {
98
                // Exception did not contain a valid code
99 10
                $response = $response->withStatus(500);
100
            }
101
102 13
            if ($e instanceof HttpException) {
103 2
                $response = $e->withResponse($response);
104 2
            }
105
106 13
            if ($this->preferences->displayDebug()) {
107
108
                $handler = $this->handler($type);
109
                $this->whoops->pushHandler($handler);
0 ignored issues
show
Documentation introduced by
$handler is of type object<Whoops\Handler\HandlerInterface>, but the function expects a callable.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
110
111
                $body = $this->whoops->handleException($e);
0 ignored issues
show
Documentation introduced by
$e is of type object<Exception>, but the function expects a object<Throwable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
112
                $response->getBody()->write($body);
0 ignored issues
show
Security Bug introduced by
It seems like $body defined by $this->whoops->handleException($e) on line 111 can also be of type false; however, Psr\Http\Message\StreamInterface::write() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
113
114
                $this->whoops->popHandler();
115
            }
116
117 13
            return $response;
118
        }
119
    }
120
121
    /**
122
     * Determine the preferred content type for the current request
123
     *
124
     * @param ServerRequestInterface $request
125
     *
126
     * @return string
127
     */
128 13
    private function type(ServerRequestInterface $request)
129
    {
130 13
        $accept = $request->getHeaderLine('Accept');
131 13
        $priorities = $this->preferences->toArray();
132
133 13
        if (!empty($accept)) {
134 10
            $preferred = $this->negotiator->getBest($accept, array_keys($priorities));
135 10
        }
136
137 13
        if (!empty($preferred)) {
138 10
            return $preferred->getValue();
139
        }
140
141 3
        return key($priorities);
142
    }
143
144
    /**
145
     * Retrieve the handler to use for the given type
146
     *
147
     * @param string $type
148
     *
149
     * @return \Whoops\Handler\HandlerInterface
150
     */
151
    private function handler($type)
152
    {
153
        return call_user_func($this->resolver, $this->preferences[$type]);
154
    }
155
}
156