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); |
|
|
|
|
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
|
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()); |
|
|
|
|
112
|
|
|
break; |
113
|
1 |
|
case 'json': |
114
|
|
|
$whoops->pushHandler(new JsonResponseHandler()); |
|
|
|
|
115
|
|
|
break; |
116
|
1 |
|
default: |
117
|
1 |
|
$whoops->pushHandler(new PlainTextHandler()); |
|
|
|
|
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
|
|
|
|
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.