-
-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathErrorException.php
More file actions
176 lines (155 loc) · 5.28 KB
/
ErrorException.php
File metadata and controls
176 lines (155 loc) · 5.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
<?php
declare(strict_types=1);
namespace Yiisoft\ErrorHandler\Exception;
use Exception;
use ReflectionProperty;
use Yiisoft\FriendlyException\FriendlyExceptionInterface;
use function array_slice;
use function in_array;
use function function_exists;
use function ini_get;
use const E_COMPILE_ERROR;
use const E_COMPILE_WARNING;
use const E_CORE_ERROR;
use const E_CORE_WARNING;
use const E_DEPRECATED;
use const E_ERROR;
use const E_NOTICE;
use const E_PARSE;
use const E_RECOVERABLE_ERROR;
use const E_USER_DEPRECATED;
use const E_USER_ERROR;
use const E_USER_NOTICE;
use const E_USER_WARNING;
use const E_WARNING;
/**
* `ErrorException` represents a PHP error.
* @psalm-type DebugBacktraceType = list<array{args?: array<mixed>, class?: class-string, file?: string, function?: string, line?: int, object?: object, type?: string}>
*
* @final
*/
class ErrorException extends \ErrorException implements FriendlyExceptionInterface
{
/** @psalm-suppress MissingClassConstType Private constants never change. */
private const ERROR_NAMES = [
E_ERROR => 'PHP Fatal Error',
E_WARNING => 'PHP Warning',
E_PARSE => 'PHP Parse Error',
E_NOTICE => 'PHP Notice',
E_CORE_ERROR => 'PHP Core Error',
E_CORE_WARNING => 'PHP Core Warning',
E_COMPILE_ERROR => 'PHP Compile Error',
E_COMPILE_WARNING => 'PHP Compile Warning',
E_USER_ERROR => 'PHP User Error',
E_USER_WARNING => 'PHP User Warning',
E_USER_NOTICE => 'PHP User Notice',
2048 => 'PHP Strict Warning', // E_STRICT
E_RECOVERABLE_ERROR => 'PHP Recoverable Error',
E_DEPRECATED => 'PHP Deprecated Warning',
E_USER_DEPRECATED => 'PHP User Deprecated Warning',
];
/** @psalm-param DebugBacktraceType $backtrace */
public function __construct(
string $message,
int $code,
int $severity,
string $filename,
int $line,
?Exception $previous,
private readonly array $backtrace,
) {
parent::__construct($message, $code, $severity, $filename, $line, $previous);
$this->addXDebugTraceToFatalIfAvailable();
}
/**
* Returns if error is one of fatal type.
*
* @param array $error error got from error_get_last()
*
* @return bool If error is one of fatal type.
*/
public static function isFatalError(array $error): bool
{
return isset($error['type']) && in_array(
$error['type'],
[E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING],
true,
);
}
/**
* @return string The user-friendly name of this exception.
*/
public function getName(): string
{
return self::ERROR_NAMES[$this->getCode()] ?? 'Error';
}
public function getSolution(): ?string
{
return null;
}
/**
* @psalm-return DebugBacktraceType
*/
public function getBacktrace(): array
{
return $this->backtrace;
}
/**
* Fatal errors normally do not provide any trace making it harder to debug. In case XDebug is installed, we
* can get a trace using `xdebug_get_function_stack()`.
*/
private function addXDebugTraceToFatalIfAvailable(): void
{
if ($this->isXdebugStackAvailable()) {
/**
* XDebug trace can't be modified and used directly with PHP 7
*
* @see https://github.com/yiisoft/yii2/pull/11723
*
* @psalm-var array<int,array>
*/
$xDebugTrace = array_slice(array_reverse(xdebug_get_function_stack()), 1, -1);
$trace = [];
foreach ($xDebugTrace as $frame) {
if (!isset($frame['function'])) {
$frame['function'] = 'unknown';
}
// XDebug < 2.1.1: https://bugs.xdebug.org/view.php?id=695
if (!isset($frame['type']) || $frame['type'] === 'static') {
$frame['type'] = '::';
} elseif ($frame['type'] === 'dynamic') {
$frame['type'] = '->';
}
// XDebug has a different key name
if (isset($frame['params']) && !isset($frame['args'])) {
/** @var mixed */
$frame['args'] = $frame['params'];
}
$trace[] = $frame;
}
$ref = new ReflectionProperty(Exception::class, 'trace');
$ref->setValue($this, $trace);
}
}
/**
* Ensures that Xdebug stack trace is available based on Xdebug version.
* Idea taken from developer bishopb at https://github.com/rollbar/rollbar-php
*/
private function isXdebugStackAvailable(): bool
{
if (!function_exists('\xdebug_get_function_stack')) {
return false;
}
// check for Xdebug being installed to ensure origin of xdebug_get_function_stack()
$version = phpversion('xdebug');
if ($version === false) {
return false;
}
// Xdebug 2 and prior
if (version_compare($version, '3.0.0', '<')) {
return true;
}
// Xdebug 3 and later, proper mode is required
return str_contains((string) ini_get('xdebug.mode'), 'develop');
}
}