Completed
Push — master ( 93aa4f...2cb587 )
by
unknown
02:34
created

ExceptionHandler::__invoke()   C

Complexity

Conditions 7
Paths 57

Size

Total Lines 48
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 7.5375

Importance

Changes 0
Metric Value
dl 0
loc 48
ccs 21
cts 27
cp 0.7778
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 29
nc 57
nop 3
crap 7.5375
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
                $this->logger->error($e->getMessage(), [
80
                    'exception' => $e
81 13
                ]);
82 13
            }
83
84 13
            $type = $this->type($request);
85
86 13
            $response = $response->withHeader('Content-Type', $type);
87
88
            try {
89 13
                if (method_exists($e, 'getHttpStatus')) {
90 1
                    $code = $e->getHttpStatus();
91 1
                } else {
92 12
                    $code = $e->getCode();
93
                }
94 13
                $response = $response->withStatus($code);
95 13
            } catch (InvalidArgumentException $_) {
96
                // Exception did not contain a valid code
97 10
                $response = $response->withStatus(500);
98
            }
99
100 13
            if ($e instanceof HttpException) {
101 2
                $response = $e->withResponse($response);
102 2
            }
103
104 13
            if ($this->preferences->displayDebug()) {
105
106
                $handler = $this->handler($type);
107
                $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...
108
109
                $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...
110
                $response->getBody()->write($body);
0 ignored issues
show
Security Bug introduced by
It seems like $body defined by $this->whoops->handleException($e) on line 109 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...
111
112
                $this->whoops->popHandler();
113
            }
114
115 13
            return $response;
116
        }
117
    }
118
119
    /**
120
     * Determine the preferred content type for the current request
121
     *
122
     * @param ServerRequestInterface $request
123
     *
124
     * @return string
125
     */
126 13
    private function type(ServerRequestInterface $request)
127
    {
128 13
        $accept = $request->getHeaderLine('Accept');
129 13
        $priorities = $this->preferences->toArray();
130
131 13
        if (!empty($accept)) {
132 10
            $preferred = $this->negotiator->getBest($accept, array_keys($priorities));
133 10
        }
134
135 13
        if (!empty($preferred)) {
136 10
            return $preferred->getValue();
137
        }
138
139 3
        return key($priorities);
140
    }
141
142
    /**
143
     * Retrieve the handler to use for the given type
144
     *
145
     * @param string $type
146
     *
147
     * @return \Whoops\Handler\HandlerInterface
148
     */
149
    private function handler($type)
150
    {
151
        return call_user_func($this->resolver, $this->preferences[$type]);
152
    }
153
}
154