Completed
Push — master ( e15c58...b150a8 )
by Changwan
07:08
created

DefaultHttpErrorHandler   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 134
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 75%

Importance

Changes 0
Metric Value
dl 0
loc 134
ccs 57
cts 76
cp 0.75
rs 10
c 0
b 0
f 0
wmc 23
lcom 1
cbo 9

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
C handle() 0 37 8
A isAjax() 0 5 2
A prettifyRequest() 0 13 3
A getWhoops() 0 21 3
B getAcceptType() 0 18 6
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 1
    {
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 2
            }
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
Bug introduced by
It seems like $exception defined by parameter $exception on line 39 can also be of type object<Exception>; however, Whoops\Run::handleException() does only seem to accept object<Throwable>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
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);
0 ignored issues
show
Bug introduced by
It seems like $exception defined by parameter $exception on line 39 can also be of type object<Exception>; however, Psr\Log\LoggerInterface::error() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
66 1
        }
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 3
        }
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 1
            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 1
        }
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 1
        ) {
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 1
        ) {
147
            return 'json';
148
        }
149 1
        return 'text';
150
    }
151
}
152