Passed
Push — master ( 0b616a...1a71a8 )
by Sergei
02:12
created

ErrorException::getName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\ErrorHandler\Exception;
6
7
use Exception;
8
use ReflectionProperty;
9
use Yiisoft\FriendlyException\FriendlyExceptionInterface;
10
11
use function array_slice;
12
use function in_array;
13
use function function_exists;
14
15
/**
16
 * ErrorException represents a PHP error.
17
 */
18
class ErrorException extends \ErrorException implements FriendlyExceptionInterface
19
{
20
    private const ERROR_NAMES = [
21
        E_ERROR => 'PHP Fatal Error',
22
        E_WARNING => 'PHP Warning',
23
        E_PARSE => 'PHP Parse Error',
24
        E_NOTICE => 'PHP Notice',
25
        E_CORE_ERROR => 'PHP Core Error',
26
        E_CORE_WARNING => 'PHP Core Warning',
27
        E_COMPILE_ERROR => 'PHP Compile Error',
28
        E_COMPILE_WARNING => 'PHP Compile Warning',
29
        E_USER_ERROR => 'PHP User Error',
30
        E_USER_WARNING => 'PHP User Warning',
31
        E_USER_NOTICE => 'PHP User Notice',
32
        E_STRICT => 'PHP Strict Warning',
33
        E_RECOVERABLE_ERROR => 'PHP Recoverable Error',
34
        E_DEPRECATED => 'PHP Deprecated Warning',
35
        E_USER_DEPRECATED => 'PHP User Deprecated Warning',
36
    ];
37
38 3
    public function __construct(
39
        string $message = '',
40
        int $code = 0,
41
        int $severity = 1,
42
        string $filename = __FILE__,
43
        int $line = __LINE__,
44
        Exception $previous = null
45
    ) {
46 3
        parent::__construct($message, $code, $severity, $filename, $line, $previous);
47 3
        $this->addXDebugTraceToFatalIfAvailable();
48
    }
49
50
    /**
51
     * Returns if error is one of fatal type.
52
     *
53
     * @param array $error error got from error_get_last()
54
     *
55
     * @return bool If error is one of fatal type.
56
     */
57 1
    public static function isFatalError(array $error): bool
58
    {
59 1
        return isset($error['type']) && in_array(
60 1
            $error['type'],
61 1
            [E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING],
62 1
            true,
63 1
        );
64
    }
65
66
    /**
67
     * @return string The user-friendly name of this exception.
68
     */
69 1
    public function getName(): string
70
    {
71 1
        return self::ERROR_NAMES[$this->getCode()] ?? 'Error';
72
    }
73
74 1
    public function getSolution(): ?string
75
    {
76 1
        return null;
77
    }
78
79
    /**
80
     * Fatal errors normally do not provide any trace making it harder to debug. In case XDebug is installed, we
81
     * can get a trace using xdebug_get_function_stack().
82
     */
83 3
    private function addXDebugTraceToFatalIfAvailable(): void
84
    {
85 3
        if ($this->isXdebugStackAvailable()) {
86
            // XDebug trace can't be modified and used directly with PHP 7
87
            // @see https://github.com/yiisoft/yii2/pull/11723
88
            $xDebugTrace = array_slice(array_reverse(xdebug_get_function_stack()), 1, -1);
89
            $trace = [];
90
91
            foreach ($xDebugTrace as $frame) {
92
                if (!isset($frame['function'])) {
93
                    $frame['function'] = 'unknown';
94
                }
95
96
                // XDebug < 2.1.1: http://bugs.xdebug.org/view.php?id=695
97
                if (!isset($frame['type']) || $frame['type'] === 'static') {
98
                    $frame['type'] = '::';
99
                } elseif ($frame['type'] === 'dynamic') {
100
                    $frame['type'] = '->';
101
                }
102
103
                // XDebug has a different key name
104
                if (isset($frame['params']) && !isset($frame['args'])) {
105
                    $frame['args'] = $frame['params'];
106
                }
107
                $trace[] = $frame;
108
            }
109
110
            $ref = new ReflectionProperty('Exception', 'trace');
111
            $ref->setAccessible(true);
112
            $ref->setValue($this, $trace);
113
        }
114
    }
115
116
    /**
117
     * Ensures that Xdebug stack trace is available based on Xdebug version.
118
     * Idea taken from developer bishopb at https://github.com/rollbar/rollbar-php
119
     */
120 3
    private function isXdebugStackAvailable(): bool
121
    {
122 3
        if (!function_exists('\xdebug_get_function_stack')) {
123
            return false;
124
        }
125
126
        // check for Xdebug being installed to ensure origin of xdebug_get_function_stack()
127 3
        $version = phpversion('xdebug');
128
129 3
        if ($version === false) {
130
            return false;
131
        }
132
133
        // Xdebug 2 and prior
134 3
        if (version_compare($version, '3.0.0', '<')) {
135
            return true;
136
        }
137
138
        // Xdebug 3 and later, proper mode is required
139 3
        return false !== strpos(ini_get('xdebug.mode'), 'develop');
140
    }
141
}
142