ErrorHandler::onShutdown()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 0
cts 10
cp 0
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 9
nc 2
nop 0
crap 12
1
<?php
2
3
declare(strict_types=1);
4
5
namespace CentErr;
6
7
use CentErr\Emitter\EmitterInterface;
8
use ErrorException;
9
use Throwable;
10
11
class ErrorHandler implements ErrorHandlerInterface
12
{
13
    /**
14
     * @var EmitterInterface
15
     */
16
    protected $emitter;
17
18
    /**
19
     * @var callable[]
20
     */
21
    protected $processors;
22
23
    /**
24
     * @var array
25
     */
26
    protected $options = [
27
        'blockingErrors' => true,
28
    ];
29
30
    /**
31
     * @var bool
32
     */
33
    protected $treatErrorsAsExceptions = false;
34
35
    /**
36
     * @var bool
37
     */
38
    private $registered = false;
39
40 11
    public function __construct(EmitterInterface $emitter, array $processors = [], array $options = [])
41
    {
42 11
        $this->emitter = $emitter;
43 11
        $this->processors = $processors;
44 11
        $this->options = array_merge($this->options, $options);
45
46 11
        $this->treatErrorsAsExceptions = $this->options['blockingErrors'];
47 11
    }
48
49 6
    final public function register() : void
50
    {
51 6
        if ($this->registered) {
52
            return;
53
        }
54
55 6
        set_error_handler([$this, 'handleError']);
56 6
        set_exception_handler([$this, 'handleException']);
57 6
        register_shutdown_function([$this, 'onShutdown']);
58
59 6
        $this->registered = true;
60 6
    }
61
62 6
    final public function unregister() : void
63
    {
64 6
        if (!$this->registered) {
65
            return;
66
        }
67
68 6
        restore_error_handler();
69 6
        restore_exception_handler();
70
71 6
        $this->registered = false;
72 6
    }
73
74 2
    final public function isRegistered() : bool
75
    {
76 2
        return $this->registered;
77
    }
78
79 6
    public function handleError(int $level, string $message, string  $file = null, int $line = null) : bool
80
    {
81 6
        if (! ($level & error_reporting())) {
82 3
            return false;
83
        }
84
85 3
        $exception = new ErrorException($message, 0, $level, $file, $line);
86
87 3
        if ($this->treatErrorsAsExceptions) {
88 2
            throw $exception;
89
        }
90
91 1
        $this->handleException($exception);
92
93 1
        if ($this->isFatalError($level)) {
94
            $this->terminate();
95
        }
96
97 1
        return true;
98
    }
99
100 4
    public function handleException(Throwable $exception) : void
101
    {
102 4
        $exception = $this->process($exception);
103
104 4
        $this->emit($exception);
105 4
    }
106
107
    public function onShutdown() : void
108
    {
109
        $this->treatErrorsAsExceptions = false;
110
111
        $error = error_get_last();
112
113
        if ($error && $this->isFatalError($error['type'])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $error of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
114
            $this->handleError(
115
                $error['type'],
116
                $error['message'],
117
                $error['file'],
118
                $error['line']
119
            );
120
        }
121
    }
122
123 4
    protected function process(Throwable $exception) : Throwable
124
    {
125 4
        foreach ($this->processors as $processor) {
126 1
            $exception = $processor($exception);
127
        }
128
129 4
        return $exception;
130
    }
131
132 4
    protected function emit(Throwable $exception) : void
133
    {
134 4
        $this->emitter->emit($exception);
135 4
    }
136
137 1
    final protected function isFatalError(int $errorLevel) : bool
138
    {
139 1
        $fatalErrorLevels = E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR;
140
141 1
        return ($errorLevel & $fatalErrorLevels) > 0;
142
    }
143
144
    final protected function terminate() : void
145
    {
146
        exit(1);
147
    }
148
}
149