Completed
Push — master ( 8155ff...9fff48 )
by Changwan
03:00
created

DefaultHttpErrorHandler::handle()   C

Complexity

Conditions 8
Paths 15

Size

Total Lines 37
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 9.8645

Importance

Changes 0
Metric Value
cc 8
eloc 26
nc 15
nop 2
dl 0
loc 37
ccs 18
cts 26
cp 0.6923
crap 9.8645
rs 5.3846
c 0
b 0
f 0
1
<?php
2
namespace Wandu\Foundation\Error;
3
4
use Psr\Http\Message\ServerRequestInterface;
5
use Psr\Log\LoggerInterface;
6
use Wandu\Config\Contracts\ConfigInterface;
7
use Wandu\Foundation\Contracts\HttpErrorHandlerInterface;
8
use Wandu\Http\Exception\HttpException;
9
use Wandu\Router\Exception\MethodNotAllowedException as RouteMethodException;
10
use Wandu\Router\Exception\RouteNotFoundException;
11
use Whoops\Handler\JsonResponseHandler;
12
use Whoops\Handler\PlainTextHandler;
13
use Whoops\Handler\PrettyPageHandler;
14
use Whoops\Run;
15
use function Wandu\Http\Response\create;
16
use function Wandu\Http\Response\json;
17
18
class DefaultHttpErrorHandler implements HttpErrorHandlerInterface
19
{
20
    /** @var \Psr\Log\LoggerInterface */
21
    protected $logger;
22
    
23
    /** @var \Wandu\Config\Contracts\ConfigInterface */
24
    protected $config;
25
    
26
    /**
27
     * @param \Wandu\Config\Contracts\ConfigInterface $config
28
     * @param \Psr\Log\LoggerInterface $logger
29
     */
30 4
    public function __construct(ConfigInterface $config, LoggerInterface $logger = null)
31
    {
32 4
        $this->config = $config;
33 4
        $this->logger = $logger;
34 4
    }
35
36
    /**
37
     * {@inheritdoc}
38
     */
39 4
    public function handle(ServerRequestInterface $request, $exception)
40
    {
41 4
        if ($exception instanceof HttpException) {
42 2
            if ($this->logger) {
43 2
                $this->logger->notice($this->prettifyRequest($request));
44 2
                $this->logger->notice($exception);
45
            }
46 2
            return $exception;
47
        }
48 2
        if ($this->config->get('debug', true)) {
49 1
            $whoops = $this->getWhoops($request);
50 1
            return create($whoops->handleException($exception), 500);
0 ignored issues
show
Security Bug introduced by
It seems like $whoops->handleException($exception) targeting Whoops\Run::handleException() can also be of type false; however, Wandu\Http\Response\create() does only seem to accept string|null, did you maybe forget to handle an error condition?
Loading history...
51
        }
52
53 1
        $statusCode = 500;
54 1
        $reasonPhrase = 'Internal Server Error';
55
56 1
        if ($exception instanceof RouteNotFoundException) {
57
            $statusCode = 404;
58
            $reasonPhrase = "Not Found";
59 1
        } elseif ($exception instanceof RouteMethodException) {
60
            $statusCode = 405;
61
            $reasonPhrase = 'Method Not Allowed';
62
        }
63 1
        if ($this->logger) {
64 1
            $this->logger->error($this->prettifyRequest($request));
65 1
            $this->logger->error($exception);
66
        }
67
68 1
        if ($this->isAjax($request)) {
69
            return json([
70
                'status' => $statusCode,
71
                'reason' => $reasonPhrase,
72
            ], $statusCode);
73
        }
74 1
        return create("{$statusCode} {$reasonPhrase}", $statusCode);
75
    }
76
77
    /**
78
     * @param \Psr\Http\Message\ServerRequestInterface $request
79
     * @return bool
80
     */
81 1
    protected function isAjax(ServerRequestInterface $request)
82
    {
83 1
        return $request->hasHeader('x-requested-with') &&
84 1
            $request->getHeaderLine('x-requested-with') === 'XMLHttpRequest';
85
    }
86
87
    /**
88
     * @param \Psr\Http\Message\ServerRequestInterface $request
89
     * @return string
90
     */
91 3
    protected function prettifyRequest(ServerRequestInterface $request)
92
    {
93 3
        $contents = "{$request->getMethod()} : {$request->getUri()->__toString()}\n";
94 3
        $contents .= "HEADERS\n";
95 3
        foreach ($request->getHeaders() as $name => $value) {
96
            $contents .= "    {$name} : {$request->getHeaderLine($name)}\n";
97
        }
98 3
        if ($body = $request->getBody()) {
99
            $contents .= "BODY\n";
100
            $contents .= "\"{$body->__toString()}\"\n";
101
        }
102 3
        return $contents;
103
    }
104
    
105 1
    protected function getWhoops(ServerRequestInterface $request)
106
    {
107 1
        $whoops = new Run();
108
109 1
        switch ($this->getAcceptType($request)) {
110 1
            case 'html':
111
                $whoops->pushHandler(new PrettyPageHandler());
0 ignored issues
show
Documentation introduced by
new \Whoops\Handler\PrettyPageHandler() is of type object<Whoops\Handler\PrettyPageHandler>, 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...
112
                break;
113 1
            case 'json':
114
                $whoops->pushHandler(new JsonResponseHandler());
0 ignored issues
show
Documentation introduced by
new \Whoops\Handler\JsonResponseHandler() is of type object<Whoops\Handler\JsonResponseHandler>, 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...
115
                break;
116
            default:
117 1
                $whoops->pushHandler(new PlainTextHandler());
0 ignored issues
show
Documentation introduced by
new \Whoops\Handler\PlainTextHandler() is of type object<Whoops\Handler\PlainTextHandler>, 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...
118
        }
119
120 1
        $whoops->allowQuit(false);
121 1
        $whoops->writeToOutput(false);
122 1
        $whoops->sendHttpCode(false);
123
124 1
        return $whoops;
125
    }
126
127
    /**
128
     * @ref github.com/oscarotero/psr7-middlewares/blob/master/src/Middleware/FormatNegotiator.php
129
     *
130
     * @param \Psr\Http\Message\ServerRequestInterface $request
131
     * @return string
132
     */
133 1
    protected function getAcceptType(ServerRequestInterface $request)
134
    {
135 1
        $accept = $request->getHeaderLine('Accept');
136
        if (
137 1
            strpos($accept, 'text/html') !== false ||
138 1
            strpos($accept, 'application/xhtml+xml') !== false
139
        ) {
140
            return 'html';
141
        }
142
        if (
143 1
            strpos($accept, 'application/json') !== false ||
144 1
            strpos($accept, 'text/json') !== false ||
145 1
            strpos($accept, 'application/x-json') !== false
146
        ) {
147
            return 'json';
148
        }
149 1
        return 'text';
150
    }
151
}
152