1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Nord\Lumen\Core\Debug; |
4
|
|
|
|
5
|
|
|
use Exception; |
6
|
|
|
use Illuminate\Http\Exceptions\HttpResponseException; |
7
|
|
|
use Illuminate\Http\JsonResponse; |
8
|
|
|
use Illuminate\Http\Response; |
9
|
|
|
use League\OAuth2\Server\Exception\OAuthException; |
10
|
|
|
use Symfony\Component\HttpKernel\Exception\HttpException; |
11
|
|
|
|
12
|
|
|
class JsonExceptionHandler |
13
|
|
|
{ |
14
|
|
|
/** |
15
|
|
|
* @var bool |
16
|
|
|
*/ |
17
|
|
|
private $debug; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* JsonExceptionHandler constructor. |
21
|
|
|
* |
22
|
|
|
* @param bool $debug |
23
|
|
|
*/ |
24
|
|
|
public function __construct($debug) |
25
|
|
|
{ |
26
|
|
|
$this->debug = $debug; |
27
|
|
|
} |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* @param Exception $exception |
31
|
|
|
* |
32
|
|
|
* @return JsonResponse |
33
|
|
|
*/ |
34
|
|
|
public function createResponse(Exception $exception) |
35
|
|
|
{ |
36
|
|
|
$data = $this->createResponseData($exception); |
37
|
|
|
$status = $this->resolveResponseStatusCode($exception); |
38
|
|
|
|
39
|
|
|
return new JsonResponse($data, $status, [], JSON_PARTIAL_OUTPUT_ON_ERROR); |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @param Exception $exception |
44
|
|
|
* |
45
|
|
|
* @return array |
46
|
|
|
*/ |
47
|
|
|
protected function createResponseData(Exception $exception) |
48
|
|
|
{ |
49
|
|
|
return $this->debug |
50
|
|
|
? $this->extractExceptionData($exception) |
51
|
|
|
: $this->createDefaultResponseData($exception); |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* @param Exception $exception |
56
|
|
|
* |
57
|
|
|
* @return array |
58
|
|
|
*/ |
59
|
|
|
protected function extractExceptionData(Exception $exception) |
60
|
|
|
{ |
61
|
|
|
$data = [ |
62
|
|
|
'exception' => get_class($exception), |
63
|
|
|
'message' => $exception->getMessage(), |
64
|
|
|
'code' => $exception->getCode(), |
65
|
|
|
'file' => $exception->getFile(), |
66
|
|
|
'line' => $exception->getLine(), |
67
|
|
|
'trace' => [] |
68
|
|
|
]; |
69
|
|
|
|
70
|
|
|
foreach ($exception->getTrace() as $item) { |
71
|
|
|
if (isset($item['args']) && is_array($item['args'])) { |
72
|
|
|
$item['args'] = $this->cleanTraceArgs($item['args']); |
73
|
|
|
} |
74
|
|
|
$data['trace'][] = $item; |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
return $data; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* @param Exception $exception |
82
|
|
|
* |
83
|
|
|
* @return array |
84
|
|
|
*/ |
85
|
|
|
protected function createDefaultResponseData(Exception $exception) |
86
|
|
|
{ |
87
|
|
|
$statusCode = $this->resolveResponseStatusCode($exception); |
88
|
|
|
if ($statusCode === 404) { |
89
|
|
|
return ['message' => 'Sorry, the page you are looking for could not be found.']; |
90
|
|
|
} else { |
91
|
|
|
return ['message' => 'Whoops, looks like something went wrong.']; |
92
|
|
|
} |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* @param Exception $exception |
97
|
|
|
* |
98
|
|
|
* @return int |
99
|
|
|
*/ |
100
|
|
|
protected function resolveResponseStatusCode(Exception $exception) |
101
|
|
|
{ |
102
|
|
|
if ($exception instanceof HttpResponseException) { |
103
|
|
|
return $exception->getCode(); |
104
|
|
|
} elseif ($exception instanceof HttpException) { |
105
|
|
|
return $exception->getStatusCode(); |
106
|
|
|
} elseif ($exception instanceof OAuthException) { |
|
|
|
|
107
|
|
|
return $exception->httpStatusCode; |
108
|
|
|
} else { |
109
|
|
|
return 500; |
110
|
|
|
} |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* @param array $args |
115
|
|
|
* @param int $level |
116
|
|
|
* @param int $count |
117
|
|
|
* |
118
|
|
|
* @return array |
119
|
|
|
*/ |
120
|
|
|
private function cleanTraceArgs(array $args, $level = 0, &$count = 0) |
121
|
|
|
{ |
122
|
|
|
$result = array(); |
123
|
|
|
|
124
|
|
|
foreach ($args as $key => $value) { |
125
|
|
|
if (++$count > 1e4) { |
126
|
|
|
return '*SKIPPED over 10000 entries*'; |
127
|
|
|
} |
128
|
|
|
if (is_object($value)) { |
129
|
|
|
$result[$key] = get_class($value); |
130
|
|
|
} elseif (is_array($value)) { |
131
|
|
|
if ($level > 10) { |
132
|
|
|
$result[$key] = '*DEEP NESTED ARRAY*'; |
133
|
|
|
} else { |
134
|
|
|
$result[$key] = $this->cleanTraceArgs($value, $level + 1, $count); |
135
|
|
|
} |
136
|
|
|
} elseif (is_null($value)) { |
137
|
|
|
$result[$key] = null; |
138
|
|
|
} elseif (is_bool($value)) { |
139
|
|
|
$result[$key] = $value; |
140
|
|
|
} elseif (is_int($value)) { |
141
|
|
|
$result[$key] = $value; |
142
|
|
|
} elseif (is_resource($value)) { |
143
|
|
|
$result[$key] = get_resource_type($value); |
144
|
|
|
} elseif ($value instanceof \__PHP_Incomplete_Class) { |
145
|
|
|
$array = new \ArrayObject($value); |
146
|
|
|
$result[$key] = $array['__PHP_Incomplete_Class_Name']; |
147
|
|
|
} else { |
148
|
|
|
$result[$key] = (string)$value; |
149
|
|
|
} |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
return $result; |
153
|
|
|
} |
154
|
|
|
} |
155
|
|
|
|
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.json
file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.json
to be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
require
orrequire-dev
section?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceof
checks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.