Passed
Push — master ( eb24b8...367477 )
by Mathieu
12:15
created

Error::addErrorHandler()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Suricate;
6
7
use ErrorException;
8
9
/**
10
 * @property bool $report
11
 * @property bool $dumpContext
12
 * @property mixed $httpHandler Handler (object/closure/string) in charge of the error
13
 */
14
class Error extends Service
15
{
16
    protected $parametersList = ['report', 'dumpContext', 'httpHandler'];
17
    protected $errorHandlers = [];
18
19
    public static function handleException($e, $context = null)
20
    {
21
        if ($e instanceof Exception\HttpException) {
22
            $httpHandler = Suricate::Error()->httpHandler;
23
            if (is_object($httpHandler) && $httpHandler instanceof \Closure) {
24
                $httpHandler($e);
25
                return;
26
            } elseif ($httpHandler != '') {
27
                $httpHandler = explode('::', $httpHandler);
28
29
                if (count($httpHandler) > 1) {
30
                    $userFunc = $httpHandler;
31
                } else {
32
                    $userFunc = head($httpHandler);
33
                }
34
                if (is_callable($userFunc)) {
35
                    call_user_func($userFunc, $e);
36
                }
37
                return;
38
            }
39
40
            Suricate::Error()->displayGenericHttpExceptionPage($e);
41
        }
42
43
        while (ob_get_level() > 1) {
44
            ob_end_clean();
45
        }
46
47
        $json = [];
48
        $error = $e;
49
        do {
50
            $json[] = [
51
                'type' => get_class($error),
52
                'code' => $error->getCode(),
53
                'message' => $error->getMessage(),
54
                'file' => $error->getFile(),
55
                'line' => $error->getLine(),
56
                'trace' => explode("\n", $error->getTraceAsString())
57
            ];
58
        } while (($error = $error->getPrevious()));
59
        Suricate::Logger()->error(json_encode($json));
60
61
        Suricate::Error()->displayExceptionPage($e, $context);
62
    }
63
64
    public static function handleError(
65
        $code,
66
        $message,
67
        $file,
68
        $line,
69
        $context = null
70
    ) {
71
        foreach (Suricate::Error()->getErrorHandlers() as $customHandler) {
72
            $customHandler(new ErrorException($message, $code, 0, $file, $line));
73
        }
74
        if (!(error_reporting() & $code)) {
75
            return false; // Silenced
76
        }
77
78
        static::handleException(
79
            new ErrorException($message, $code, 0, $file, $line),
80
            $context
81
        );
82
    }
83
84
    public static function handleShutdownError()
85
    {
86
        if (
87
            !is_null(($error = error_get_last())) &&
88
            static::isFatal($error['type'])
89
        ) {
90
            static::handleException(
91
                new ErrorException(
92
                    $error['message'],
93
                    $error['type'],
94
                    0,
95
                    $error['file'],
96
                    $error['line']
97
                )
98
            );
99
        }
100
    }
101
102
    private function displayExceptionPage($e, $context = null)
103
    {
104
        if ($this->report || $this->report === null) {
105
            echo '<html>' . "\n";
106
            echo '  <head>' . "\n";
107
            echo '      <title>Oops, Uncaught Exception</title>' . "\n";
108
            echo '      <style>' . "\n";
109
            echo '          body{font-family: "Open Sans",arial,sans-serif; background: #FFF; color:#333; margin:2em}' .
110
                "\n";
111
            echo '          code{background:#E0941B;border-radius:4px;padding:2px 6px}' .
112
                "\n";
113
            echo '      </style>' . "\n";
114
            echo '  </head>' . "\n";
115
            echo '  <body>' . "\n";
116
            echo '      <h1>Oops, uncaught exception !</h1>' . "\n";
117
            echo '      <h2>This is embarrassing, but server made a booboo</h2>' .
118
                "\n";
119
            echo '      <p><code>' . $e->getMessage() . '</code></p>' . "\n";
120
            echo '      <h3>From:</h3>' . "\n";
121
            echo '      <p><code>' .
122
                $e->getFile() .
123
                ' on line ' .
124
                $e->getLine() .
125
                '</code></p>' .
126
                "\n";
127
            echo '      <h3>Call stack</h3>' . "\n";
128
            echo '      <pre>' . $e->getTraceAsString() . '</pre>' . "\n";
129
            if ($this->dumpContext) {
130
                echo '<h3>Context:</h3>';
131
                _p($context);
132
            }
133
            echo '  </body>' . "\n";
134
            echo '</html>';
135
        } else {
136
            if ($e->getCode() <= 1) {
137
                $err = new Exception\HttpException(
138
                    '500',
139
                    'Internal server error'
140
                );
141
                $this->displayGenericHttpExceptionPage($err);
142
            }
143
        }
144
        exit(1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
145
    }
146
147
    private function displayGenericHttpExceptionPage($e)
148
    {
149
        $response = Suricate::Request();
150
151
        if (
152
            is_readable(
153
                app_path() . '/views/Errors/' . $e->getStatusCode() . '.php'
154
            )
155
        ) {
156
            ob_start();
157
            include app_path() .
158
                '/views/Errors/' .
159
                $e->getStatusCode() .
160
                '.php';
161
            $body = ob_get_clean();
162
        } else {
163
            $innerHtml = '<h1>' . $e->getStatusCode() . '</h1>';
164
165
            $page = new Page();
166
            $body = $page->setTitle($e->getStatusCode())->render($innerHtml);
167
        }
168
169
        $response->setBody($body)->setHttpCode($e->getStatusCode());
170
        foreach ($e->getHeaders() as $header => $value) {
171
            $response->addHeader($header, $value);
172
        }
173
174
        $response->write();
175
        die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
176
    }
177
178
    public static function isFatal($errorType)
179
    {
180
        return in_array($errorType, [
181
            E_COMPILE_ERROR,
182
            E_CORE_ERROR,
183
            E_ERROR,
184
            E_PARSE
185
        ]);
186
    }
187
188
    public function getErrorHandlers(): array
189
    {
190
        return $this->errorHandlers;
191
    }
192
193
    public function addErrorHandler(callable $handler)
194
    {
195
        $this->errorHandlers[] = $handler;
196
    }
197
}
198