Vous êtes connecté en tant que anonymous Se Deconnecter
Browse code

Application modulaire fonctionnelle !

Emmanuel ROY authored on 12/08/2019 15:10:25
Showing 1 changed files
1 1
deleted file mode 100644
... ...
@@ -1,715 +0,0 @@
1
-<?php
2
-
3
-/*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
-namespace Symfony\Component\Debug;
13
-
14
-use Psr\Log\LoggerInterface;
15
-use Psr\Log\LogLevel;
16
-use Symfony\Component\Debug\Exception\FatalErrorException;
17
-use Symfony\Component\Debug\Exception\FatalThrowableError;
18
-use Symfony\Component\Debug\Exception\FlattenException;
19
-use Symfony\Component\Debug\Exception\OutOfMemoryException;
20
-use Symfony\Component\Debug\Exception\SilencedErrorContext;
21
-use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
22
-use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface;
23
-use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler;
24
-use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
25
-
26
-/**
27
- * A generic ErrorHandler for the PHP engine.
28
- *
29
- * Provides five bit fields that control how errors are handled:
30
- * - thrownErrors: errors thrown as \ErrorException
31
- * - loggedErrors: logged errors, when not @-silenced
32
- * - scopedErrors: errors thrown or logged with their local context
33
- * - tracedErrors: errors logged with their stack trace
34
- * - screamedErrors: never @-silenced errors
35
- *
36
- * Each error level can be logged by a dedicated PSR-3 logger object.
37
- * Screaming only applies to logging.
38
- * Throwing takes precedence over logging.
39
- * Uncaught exceptions are logged as E_ERROR.
40
- * E_DEPRECATED and E_USER_DEPRECATED levels never throw.
41
- * E_RECOVERABLE_ERROR and E_USER_ERROR levels always throw.
42
- * Non catchable errors that can be detected at shutdown time are logged when the scream bit field allows so.
43
- * As errors have a performance cost, repeated errors are all logged, so that the developer
44
- * can see them and weight them as more important to fix than others of the same level.
45
- *
46
- * @author Nicolas Grekas <p@tchwork.com>
47
- * @author Grégoire Pineau <lyrixx@lyrixx.info>
48
- *
49
- * @final since Symfony 4.3
50
- */
51
-class ErrorHandler
52
-{
53
-    private $levels = [
54
-        E_DEPRECATED => 'Deprecated',
55
-        E_USER_DEPRECATED => 'User Deprecated',
56
-        E_NOTICE => 'Notice',
57
-        E_USER_NOTICE => 'User Notice',
58
-        E_STRICT => 'Runtime Notice',
59
-        E_WARNING => 'Warning',
60
-        E_USER_WARNING => 'User Warning',
61
-        E_COMPILE_WARNING => 'Compile Warning',
62
-        E_CORE_WARNING => 'Core Warning',
63
-        E_USER_ERROR => 'User Error',
64
-        E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
65
-        E_COMPILE_ERROR => 'Compile Error',
66
-        E_PARSE => 'Parse Error',
67
-        E_ERROR => 'Error',
68
-        E_CORE_ERROR => 'Core Error',
69
-    ];
70
-
71
-    private $loggers = [
72
-        E_DEPRECATED => [null, LogLevel::INFO],
73
-        E_USER_DEPRECATED => [null, LogLevel::INFO],
74
-        E_NOTICE => [null, LogLevel::WARNING],
75
-        E_USER_NOTICE => [null, LogLevel::WARNING],
76
-        E_STRICT => [null, LogLevel::WARNING],
77
-        E_WARNING => [null, LogLevel::WARNING],
78
-        E_USER_WARNING => [null, LogLevel::WARNING],
79
-        E_COMPILE_WARNING => [null, LogLevel::WARNING],
80
-        E_CORE_WARNING => [null, LogLevel::WARNING],
81
-        E_USER_ERROR => [null, LogLevel::CRITICAL],
82
-        E_RECOVERABLE_ERROR => [null, LogLevel::CRITICAL],
83
-        E_COMPILE_ERROR => [null, LogLevel::CRITICAL],
84
-        E_PARSE => [null, LogLevel::CRITICAL],
85
-        E_ERROR => [null, LogLevel::CRITICAL],
86
-        E_CORE_ERROR => [null, LogLevel::CRITICAL],
87
-    ];
88
-
89
-    private $thrownErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED
90
-    private $scopedErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED
91
-    private $tracedErrors = 0x77FB; // E_ALL - E_STRICT - E_PARSE
92
-    private $screamedErrors = 0x55; // E_ERROR + E_CORE_ERROR + E_COMPILE_ERROR + E_PARSE
93
-    private $loggedErrors = 0;
94
-    private $traceReflector;
95
-
96
-    private $isRecursive = 0;
97
-    private $isRoot = false;
98
-    private $exceptionHandler;
99
-    private $bootstrappingLogger;
100
-
101
-    private static $reservedMemory;
102
-    private static $toStringException = null;
103
-    private static $silencedErrorCache = [];
104
-    private static $silencedErrorCount = 0;
105
-    private static $exitCode = 0;
106
-
107
-    /**
108
-     * Registers the error handler.
109
-     *
110
-     * @param self|null $handler The handler to register
111
-     * @param bool      $replace Whether to replace or not any existing handler
112
-     *
113
-     * @return self The registered error handler
114
-     */
115
-    public static function register(self $handler = null, $replace = true)
116
-    {
117
-        if (null === self::$reservedMemory) {
118
-            self::$reservedMemory = str_repeat('x', 10240);
119
-            register_shutdown_function(__CLASS__.'::handleFatalError');
120
-        }
121
-
122
-        if ($handlerIsNew = null === $handler) {
123
-            $handler = new static();
124
-        }
125
-
126
-        if (null === $prev = set_error_handler([$handler, 'handleError'])) {
127
-            restore_error_handler();
128
-            // Specifying the error types earlier would expose us to https://bugs.php.net/63206
129
-            set_error_handler([$handler, 'handleError'], $handler->thrownErrors | $handler->loggedErrors);
130
-            $handler->isRoot = true;
131
-        }
132
-
133
-        if ($handlerIsNew && \is_array($prev) && $prev[0] instanceof self) {
134
-            $handler = $prev[0];
135
-            $replace = false;
136
-        }
137
-        if (!$replace && $prev) {
138
-            restore_error_handler();
139
-            $handlerIsRegistered = \is_array($prev) && $handler === $prev[0];
140
-        } else {
141
-            $handlerIsRegistered = true;
142
-        }
143
-        if (\is_array($prev = set_exception_handler([$handler, 'handleException'])) && $prev[0] instanceof self) {
144
-            restore_exception_handler();
145
-            if (!$handlerIsRegistered) {
146
-                $handler = $prev[0];
147
-            } elseif ($handler !== $prev[0] && $replace) {
148
-                set_exception_handler([$handler, 'handleException']);
149
-                $p = $prev[0]->setExceptionHandler(null);
150
-                $handler->setExceptionHandler($p);
151
-                $prev[0]->setExceptionHandler($p);
152
-            }
153
-        } else {
154
-            $handler->setExceptionHandler($prev);
155
-        }
156
-
157
-        $handler->throwAt(E_ALL & $handler->thrownErrors, true);
158
-
159
-        return $handler;
160
-    }
161
-
162
-    public function __construct(BufferingLogger $bootstrappingLogger = null)
163
-    {
164
-        if ($bootstrappingLogger) {
165
-            $this->bootstrappingLogger = $bootstrappingLogger;
166
-            $this->setDefaultLogger($bootstrappingLogger);
167
-        }
168
-        $this->traceReflector = new \ReflectionProperty('Exception', 'trace');
169
-        $this->traceReflector->setAccessible(true);
170
-    }
171
-
172
-    /**
173
-     * Sets a logger to non assigned errors levels.
174
-     *
175
-     * @param LoggerInterface $logger  A PSR-3 logger to put as default for the given levels
176
-     * @param array|int       $levels  An array map of E_* to LogLevel::* or an integer bit field of E_* constants
177
-     * @param bool            $replace Whether to replace or not any existing logger
178
-     */
179
-    public function setDefaultLogger(LoggerInterface $logger, $levels = E_ALL, $replace = false)
180
-    {
181
-        $loggers = [];
182
-
183
-        if (\is_array($levels)) {
184
-            foreach ($levels as $type => $logLevel) {
185
-                if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] === $this->bootstrappingLogger) {
186
-                    $loggers[$type] = [$logger, $logLevel];
187
-                }
188
-            }
189
-        } else {
190
-            if (null === $levels) {
191
-                $levels = E_ALL;
192
-            }
193
-            foreach ($this->loggers as $type => $log) {
194
-                if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) {
195
-                    $log[0] = $logger;
196
-                    $loggers[$type] = $log;
197
-                }
198
-            }
199
-        }
200
-
201
-        $this->setLoggers($loggers);
202
-    }
203
-
204
-    /**
205
-     * Sets a logger for each error level.
206
-     *
207
-     * @param array $loggers Error levels to [LoggerInterface|null, LogLevel::*] map
208
-     *
209
-     * @return array The previous map
210
-     *
211
-     * @throws \InvalidArgumentException
212
-     */
213
-    public function setLoggers(array $loggers)
214
-    {
215
-        $prevLogged = $this->loggedErrors;
216
-        $prev = $this->loggers;
217
-        $flush = [];
218
-
219
-        foreach ($loggers as $type => $log) {
220
-            if (!isset($prev[$type])) {
221
-                throw new \InvalidArgumentException('Unknown error type: '.$type);
222
-            }
223
-            if (!\is_array($log)) {
224
-                $log = [$log];
225
-            } elseif (!\array_key_exists(0, $log)) {
226
-                throw new \InvalidArgumentException('No logger provided');
227
-            }
228
-            if (null === $log[0]) {
229
-                $this->loggedErrors &= ~$type;
230
-            } elseif ($log[0] instanceof LoggerInterface) {
231
-                $this->loggedErrors |= $type;
232
-            } else {
233
-                throw new \InvalidArgumentException('Invalid logger provided');
234
-            }
235
-            $this->loggers[$type] = $log + $prev[$type];
236
-
237
-            if ($this->bootstrappingLogger && $prev[$type][0] === $this->bootstrappingLogger) {
238
-                $flush[$type] = $type;
239
-            }
240
-        }
241
-        $this->reRegister($prevLogged | $this->thrownErrors);
242
-
243
-        if ($flush) {
244
-            foreach ($this->bootstrappingLogger->cleanLogs() as $log) {
245
-                $type = $log[2]['exception'] instanceof \ErrorException ? $log[2]['exception']->getSeverity() : E_ERROR;
246
-                if (!isset($flush[$type])) {
247
-                    $this->bootstrappingLogger->log($log[0], $log[1], $log[2]);
248
-                } elseif ($this->loggers[$type][0]) {
249
-                    $this->loggers[$type][0]->log($this->loggers[$type][1], $log[1], $log[2]);
250
-                }
251
-            }
252
-        }
253
-
254
-        return $prev;
255
-    }
256
-
257
-    /**
258
-     * Sets a user exception handler.
259
-     *
260
-     * @param callable $handler A handler that will be called on Exception
261
-     *
262
-     * @return callable|null The previous exception handler
263
-     */
264
-    public function setExceptionHandler(callable $handler = null)
265
-    {
266
-        $prev = $this->exceptionHandler;
267
-        $this->exceptionHandler = $handler;
268
-
269
-        return $prev;
270
-    }
271
-
272
-    /**
273
-     * Sets the PHP error levels that throw an exception when a PHP error occurs.
274
-     *
275
-     * @param int  $levels  A bit field of E_* constants for thrown errors
276
-     * @param bool $replace Replace or amend the previous value
277
-     *
278
-     * @return int The previous value
279
-     */
280
-    public function throwAt($levels, $replace = false)
281
-    {
282
-        $prev = $this->thrownErrors;
283
-        $this->thrownErrors = ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED;
284
-        if (!$replace) {
285
-            $this->thrownErrors |= $prev;
286
-        }
287
-        $this->reRegister($prev | $this->loggedErrors);
288
-
289
-        return $prev;
290
-    }
291
-
292
-    /**
293
-     * Sets the PHP error levels for which local variables are preserved.
294
-     *
295
-     * @param int  $levels  A bit field of E_* constants for scoped errors
296
-     * @param bool $replace Replace or amend the previous value
297
-     *
298
-     * @return int The previous value
299
-     */
300
-    public function scopeAt($levels, $replace = false)
301
-    {
302
-        $prev = $this->scopedErrors;
303
-        $this->scopedErrors = (int) $levels;
304
-        if (!$replace) {
305
-            $this->scopedErrors |= $prev;
306
-        }
307
-
308
-        return $prev;
309
-    }
310
-
311
-    /**
312
-     * Sets the PHP error levels for which the stack trace is preserved.
313
-     *
314
-     * @param int  $levels  A bit field of E_* constants for traced errors
315
-     * @param bool $replace Replace or amend the previous value
316
-     *
317
-     * @return int The previous value
318
-     */
319
-    public function traceAt($levels, $replace = false)
320
-    {
321
-        $prev = $this->tracedErrors;
322
-        $this->tracedErrors = (int) $levels;
323
-        if (!$replace) {
324
-            $this->tracedErrors |= $prev;
325
-        }
326
-
327
-        return $prev;
328
-    }
329
-
330
-    /**
331
-     * Sets the error levels where the @-operator is ignored.
332
-     *
333
-     * @param int  $levels  A bit field of E_* constants for screamed errors
334
-     * @param bool $replace Replace or amend the previous value
335
-     *
336
-     * @return int The previous value
337
-     */
338
-    public function screamAt($levels, $replace = false)
339
-    {
340
-        $prev = $this->screamedErrors;
341
-        $this->screamedErrors = (int) $levels;
342
-        if (!$replace) {
343
-            $this->screamedErrors |= $prev;
344
-        }
345
-
346
-        return $prev;
347
-    }
348
-
349
-    /**
350
-     * Re-registers as a PHP error handler if levels changed.
351
-     */
352
-    private function reRegister($prev)
353
-    {
354
-        if ($prev !== $this->thrownErrors | $this->loggedErrors) {
355
-            $handler = set_error_handler('var_dump');
356
-            $handler = \is_array($handler) ? $handler[0] : null;
357
-            restore_error_handler();
358
-            if ($handler === $this) {
359
-                restore_error_handler();
360
-                if ($this->isRoot) {
361
-                    set_error_handler([$this, 'handleError'], $this->thrownErrors | $this->loggedErrors);
362
-                } else {
363
-                    set_error_handler([$this, 'handleError']);
364
-                }
365
-            }
366
-        }
367
-    }
368
-
369
-    /**
370
-     * Handles errors by filtering then logging them according to the configured bit fields.
371
-     *
372
-     * @param int    $type    One of the E_* constants
373
-     * @param string $message
374
-     * @param string $file
375
-     * @param int    $line
376
-     *
377
-     * @return bool Returns false when no handling happens so that the PHP engine can handle the error itself
378
-     *
379
-     * @throws \ErrorException When $this->thrownErrors requests so
380
-     *
381
-     * @internal
382
-     */
383
-    public function handleError($type, $message, $file, $line)
384
-    {
385
-        // @deprecated to be removed in Symfony 5.0
386
-        if (\PHP_VERSION_ID >= 70300 && $message && '"' === $message[0] && 0 === strpos($message, '"continue') && preg_match('/^"continue(?: \d++)?" targeting switch is equivalent to "break(?: \d++)?"\. Did you mean to use "continue(?: \d++)?"\?$/', $message)) {
387
-            $type = E_DEPRECATED;
388
-        }
389
-
390
-        // Level is the current error reporting level to manage silent error.
391
-        $level = error_reporting();
392
-        $silenced = 0 === ($level & $type);
393
-        // Strong errors are not authorized to be silenced.
394
-        $level |= E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED;
395
-        $log = $this->loggedErrors & $type;
396
-        $throw = $this->thrownErrors & $type & $level;
397
-        $type &= $level | $this->screamedErrors;
398
-
399
-        if (!$type || (!$log && !$throw)) {
400
-            return !$silenced && $type && $log;
401
-        }
402
-        $scope = $this->scopedErrors & $type;
403
-
404
-        if (4 < $numArgs = \func_num_args()) {
405
-            $context = $scope ? (func_get_arg(4) ?: []) : [];
406
-        } else {
407
-            $context = [];
408
-        }
409
-
410
-        if (isset($context['GLOBALS']) && $scope) {
411
-            $e = $context;                  // Whatever the signature of the method,
412
-            unset($e['GLOBALS'], $context); // $context is always a reference in 5.3
413
-            $context = $e;
414
-        }
415
-
416
-        if (false !== strpos($message, "class@anonymous\0")) {
417
-            $logMessage = $this->levels[$type].': '.(new FlattenException())->setMessage($message)->getMessage();
418
-        } else {
419
-            $logMessage = $this->levels[$type].': '.$message;
420
-        }
421
-
422
-        if (null !== self::$toStringException) {
423
-            $errorAsException = self::$toStringException;
424
-            self::$toStringException = null;
425
-        } elseif (!$throw && !($type & $level)) {
426
-            if (!isset(self::$silencedErrorCache[$id = $file.':'.$line])) {
427
-                $lightTrace = $this->tracedErrors & $type ? $this->cleanTrace(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5), $type, $file, $line, false) : [];
428
-                $errorAsException = new SilencedErrorContext($type, $file, $line, isset($lightTrace[1]) ? [$lightTrace[0]] : $lightTrace);
429
-            } elseif (isset(self::$silencedErrorCache[$id][$message])) {
430
-                $lightTrace = null;
431
-                $errorAsException = self::$silencedErrorCache[$id][$message];
432
-                ++$errorAsException->count;
433
-            } else {
434
-                $lightTrace = [];
435
-                $errorAsException = null;
436
-            }
437
-
438
-            if (100 < ++self::$silencedErrorCount) {
439
-                self::$silencedErrorCache = $lightTrace = [];
440
-                self::$silencedErrorCount = 1;
441
-            }
442
-            if ($errorAsException) {
443
-                self::$silencedErrorCache[$id][$message] = $errorAsException;
444
-            }
445
-            if (null === $lightTrace) {
446
-                return;
447
-            }
448
-        } else {
449
-            $errorAsException = new \ErrorException($logMessage, 0, $type, $file, $line);
450
-
451
-            if ($throw || $this->tracedErrors & $type) {
452
-                $backtrace = $errorAsException->getTrace();
453
-                $lightTrace = $this->cleanTrace($backtrace, $type, $file, $line, $throw);
454
-                $this->traceReflector->setValue($errorAsException, $lightTrace);
455
-            } else {
456
-                $this->traceReflector->setValue($errorAsException, []);
457
-                $backtrace = [];
458
-            }
459
-        }
460
-
461
-        if ($throw) {
462
-            if (E_USER_ERROR & $type) {
463
-                for ($i = 1; isset($backtrace[$i]); ++$i) {
464
-                    if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function'])
465
-                        && '__toString' === $backtrace[$i]['function']
466
-                        && '->' === $backtrace[$i]['type']
467
-                        && !isset($backtrace[$i - 1]['class'])
468
-                        && ('trigger_error' === $backtrace[$i - 1]['function'] || 'user_error' === $backtrace[$i - 1]['function'])
469
-                    ) {
470
-                        // Here, we know trigger_error() has been called from __toString().
471
-                        // PHP triggers a fatal error when throwing from __toString().
472
-                        // A small convention allows working around the limitation:
473
-                        // given a caught $e exception in __toString(), quitting the method with
474
-                        // `return trigger_error($e, E_USER_ERROR);` allows this error handler
475
-                        // to make $e get through the __toString() barrier.
476
-
477
-                        foreach ($context as $e) {
478
-                            if ($e instanceof \Throwable && $e->__toString() === $message) {
479
-                                self::$toStringException = $e;
480
-
481
-                                return true;
482
-                            }
483
-                        }
484
-
485
-                        // Display the original error message instead of the default one.
486
-                        $this->handleException($errorAsException);
487
-
488
-                        // Stop the process by giving back the error to the native handler.
489
-                        return false;
490
-                    }
491
-                }
492
-            }
493
-
494
-            throw $errorAsException;
495
-        }
496
-
497
-        if ($this->isRecursive) {
498
-            $log = 0;
499
-        } else {
500
-            if (!\defined('HHVM_VERSION')) {
501
-                $currentErrorHandler = set_error_handler('var_dump');
502
-                restore_error_handler();
503
-            }
504
-
505
-            try {
506
-                $this->isRecursive = true;
507
-                $level = ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG;
508
-                $this->loggers[$type][0]->log($level, $logMessage, $errorAsException ? ['exception' => $errorAsException] : []);
509
-            } finally {
510
-                $this->isRecursive = false;
511
-
512
-                if (!\defined('HHVM_VERSION')) {
513
-                    set_error_handler($currentErrorHandler);
514
-                }
515
-            }
516
-        }
517
-
518
-        return !$silenced && $type && $log;
519
-    }
520
-
521
-    /**
522
-     * Handles an exception by logging then forwarding it to another handler.
523
-     *
524
-     * @param \Exception|\Throwable $exception An exception to handle
525
-     * @param array                 $error     An array as returned by error_get_last()
526
-     *
527
-     * @internal
528
-     */
529
-    public function handleException($exception, array $error = null)
530
-    {
531
-        if (null === $error) {
532
-            self::$exitCode = 255;
533
-        }
534
-        if (!$exception instanceof \Exception) {
535
-            $exception = new FatalThrowableError($exception);
536
-        }
537
-        $type = $exception instanceof FatalErrorException ? $exception->getSeverity() : E_ERROR;
538
-        $handlerException = null;
539
-
540
-        if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) {
541
-            if (false !== strpos($message = $exception->getMessage(), "class@anonymous\0")) {
542
-                $message = (new FlattenException())->setMessage($message)->getMessage();
543
-            }
544
-            if ($exception instanceof FatalErrorException) {
545
-                if ($exception instanceof FatalThrowableError) {
546
-                    $error = [
547
-                        'type' => $type,
548
-                        'message' => $message,
549
-                        'file' => $exception->getFile(),
550
-                        'line' => $exception->getLine(),
551
-                    ];
552
-                } else {
553
-                    $message = 'Fatal '.$message;
554
-                }
555
-            } elseif ($exception instanceof \ErrorException) {
556
-                $message = 'Uncaught '.$message;
557
-            } else {
558
-                $message = 'Uncaught Exception: '.$message;
559
-            }
560
-        }
561
-        if ($this->loggedErrors & $type) {
562
-            try {
563
-                $this->loggers[$type][0]->log($this->loggers[$type][1], $message, ['exception' => $exception]);
564
-            } catch (\Throwable $handlerException) {
565
-            }
566
-        }
567
-        if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) {
568
-            foreach ($this->getFatalErrorHandlers() as $handler) {
569
-                if ($e = $handler->handleError($error, $exception)) {
570
-                    $exception = $e;
571
-                    break;
572
-                }
573
-            }
574
-        }
575
-        $exceptionHandler = $this->exceptionHandler;
576
-        $this->exceptionHandler = null;
577
-        try {
578
-            if (null !== $exceptionHandler) {
579
-                return $exceptionHandler($exception);
580
-            }
581
-            $handlerException = $handlerException ?: $exception;
582
-        } catch (\Throwable $handlerException) {
583
-        }
584
-        if ($exception === $handlerException) {
585
-            self::$reservedMemory = null; // Disable the fatal error handler
586
-            throw $exception; // Give back $exception to the native handler
587
-        }
588
-        $this->handleException($handlerException);
589
-    }
590
-
591
-    /**
592
-     * Shutdown registered function for handling PHP fatal errors.
593
-     *
594
-     * @param array $error An array as returned by error_get_last()
595
-     *
596
-     * @internal
597
-     */
598
-    public static function handleFatalError(array $error = null)
599
-    {
600
-        if (null === self::$reservedMemory) {
601
-            return;
602
-        }
603
-
604
-        $handler = self::$reservedMemory = null;
605
-        $handlers = [];
606
-        $previousHandler = null;
607
-        $sameHandlerLimit = 10;
608
-
609
-        while (!\is_array($handler) || !$handler[0] instanceof self) {
610
-            $handler = set_exception_handler('var_dump');
611
-            restore_exception_handler();
612
-
613
-            if (!$handler) {
614
-                break;
615
-            }
616
-            restore_exception_handler();
617
-
618
-            if ($handler !== $previousHandler) {
619
-                array_unshift($handlers, $handler);
620
-                $previousHandler = $handler;
621
-            } elseif (0 === --$sameHandlerLimit) {
622
-                $handler = null;
623
-                break;
624
-            }
625
-        }
626
-        foreach ($handlers as $h) {
627
-            set_exception_handler($h);
628
-        }
629
-        if (!$handler) {
630
-            return;
631
-        }
632
-        if ($handler !== $h) {
633
-            $handler[0]->setExceptionHandler($h);
634
-        }
635
-        $handler = $handler[0];
636
-        $handlers = [];
637
-
638
-        if ($exit = null === $error) {
639
-            $error = error_get_last();
640
-        }
641
-
642
-        if ($error && $error['type'] &= E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR) {
643
-            // Let's not throw anymore but keep logging
644
-            $handler->throwAt(0, true);
645
-            $trace = isset($error['backtrace']) ? $error['backtrace'] : null;
646
-
647
-            if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) {
648
-                $exception = new OutOfMemoryException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, false, $trace);
649
-            } else {
650
-                $exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true, $trace);
651
-            }
652
-        } else {
653
-            $exception = null;
654
-        }
655
-
656
-        try {
657
-            if (null !== $exception) {
658
-                self::$exitCode = 255;
659
-                $handler->handleException($exception, $error);
660
-            }
661
-        } catch (FatalErrorException $e) {
662
-            // Ignore this re-throw
663
-        }
664
-
665
-        if ($exit && self::$exitCode) {
666
-            $exitCode = self::$exitCode;
667
-            register_shutdown_function('register_shutdown_function', function () use ($exitCode) { exit($exitCode); });
668
-        }
669
-    }
670
-
671
-    /**
672
-     * Gets the fatal error handlers.
673
-     *
674
-     * Override this method if you want to define more fatal error handlers.
675
-     *
676
-     * @return FatalErrorHandlerInterface[] An array of FatalErrorHandlerInterface
677
-     */
678
-    protected function getFatalErrorHandlers()
679
-    {
680
-        return [
681
-            new UndefinedFunctionFatalErrorHandler(),
682
-            new UndefinedMethodFatalErrorHandler(),
683
-            new ClassNotFoundFatalErrorHandler(),
684
-        ];
685
-    }
686
-
687
-    /**
688
-     * Cleans the trace by removing function arguments and the frames added by the error handler and DebugClassLoader.
689
-     */
690
-    private function cleanTrace($backtrace, $type, $file, $line, $throw)
691
-    {
692
-        $lightTrace = $backtrace;
693
-
694
-        for ($i = 0; isset($backtrace[$i]); ++$i) {
695
-            if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) {
696
-                $lightTrace = \array_slice($lightTrace, 1 + $i);
697
-                break;
698
-            }
699
-        }
700
-        if (class_exists(DebugClassLoader::class, false)) {
701
-            for ($i = \count($lightTrace) - 2; 0 < $i; --$i) {
702
-                if (DebugClassLoader::class === ($lightTrace[$i]['class'] ?? null)) {
703
-                    array_splice($lightTrace, --$i, 2);
704
-                }
705
-            }
706
-        }
707
-        if (!($throw || $this->scopedErrors & $type)) {
708
-            for ($i = 0; isset($lightTrace[$i]); ++$i) {
709
-                unset($lightTrace[$i]['args'], $lightTrace[$i]['object']);
710
-            }
711
-        }
712
-
713
-        return $lightTrace;
714
-    }
715
-}
Browse code

initial commit

Emmanuel ROY authored on 09/08/2019 08:39:02
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,715 @@
1
+<?php
2
+
3
+/*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+namespace Symfony\Component\Debug;
13
+
14
+use Psr\Log\LoggerInterface;
15
+use Psr\Log\LogLevel;
16
+use Symfony\Component\Debug\Exception\FatalErrorException;
17
+use Symfony\Component\Debug\Exception\FatalThrowableError;
18
+use Symfony\Component\Debug\Exception\FlattenException;
19
+use Symfony\Component\Debug\Exception\OutOfMemoryException;
20
+use Symfony\Component\Debug\Exception\SilencedErrorContext;
21
+use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
22
+use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface;
23
+use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler;
24
+use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
25
+
26
+/**
27
+ * A generic ErrorHandler for the PHP engine.
28
+ *
29
+ * Provides five bit fields that control how errors are handled:
30
+ * - thrownErrors: errors thrown as \ErrorException
31
+ * - loggedErrors: logged errors, when not @-silenced
32
+ * - scopedErrors: errors thrown or logged with their local context
33
+ * - tracedErrors: errors logged with their stack trace
34
+ * - screamedErrors: never @-silenced errors
35
+ *
36
+ * Each error level can be logged by a dedicated PSR-3 logger object.
37
+ * Screaming only applies to logging.
38
+ * Throwing takes precedence over logging.
39
+ * Uncaught exceptions are logged as E_ERROR.
40
+ * E_DEPRECATED and E_USER_DEPRECATED levels never throw.
41
+ * E_RECOVERABLE_ERROR and E_USER_ERROR levels always throw.
42
+ * Non catchable errors that can be detected at shutdown time are logged when the scream bit field allows so.
43
+ * As errors have a performance cost, repeated errors are all logged, so that the developer
44
+ * can see them and weight them as more important to fix than others of the same level.
45
+ *
46
+ * @author Nicolas Grekas <p@tchwork.com>
47
+ * @author Grégoire Pineau <lyrixx@lyrixx.info>
48
+ *
49
+ * @final since Symfony 4.3
50
+ */
51
+class ErrorHandler
52
+{
53
+    private $levels = [
54
+        E_DEPRECATED => 'Deprecated',
55
+        E_USER_DEPRECATED => 'User Deprecated',
56
+        E_NOTICE => 'Notice',
57
+        E_USER_NOTICE => 'User Notice',
58
+        E_STRICT => 'Runtime Notice',
59
+        E_WARNING => 'Warning',
60
+        E_USER_WARNING => 'User Warning',
61
+        E_COMPILE_WARNING => 'Compile Warning',
62
+        E_CORE_WARNING => 'Core Warning',
63
+        E_USER_ERROR => 'User Error',
64
+        E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
65
+        E_COMPILE_ERROR => 'Compile Error',
66
+        E_PARSE => 'Parse Error',
67
+        E_ERROR => 'Error',
68
+        E_CORE_ERROR => 'Core Error',
69
+    ];
70
+
71
+    private $loggers = [
72
+        E_DEPRECATED => [null, LogLevel::INFO],
73
+        E_USER_DEPRECATED => [null, LogLevel::INFO],
74
+        E_NOTICE => [null, LogLevel::WARNING],
75
+        E_USER_NOTICE => [null, LogLevel::WARNING],
76
+        E_STRICT => [null, LogLevel::WARNING],
77
+        E_WARNING => [null, LogLevel::WARNING],
78
+        E_USER_WARNING => [null, LogLevel::WARNING],
79
+        E_COMPILE_WARNING => [null, LogLevel::WARNING],
80
+        E_CORE_WARNING => [null, LogLevel::WARNING],
81
+        E_USER_ERROR => [null, LogLevel::CRITICAL],
82
+        E_RECOVERABLE_ERROR => [null, LogLevel::CRITICAL],
83
+        E_COMPILE_ERROR => [null, LogLevel::CRITICAL],
84
+        E_PARSE => [null, LogLevel::CRITICAL],
85
+        E_ERROR => [null, LogLevel::CRITICAL],
86
+        E_CORE_ERROR => [null, LogLevel::CRITICAL],
87
+    ];
88
+
89
+    private $thrownErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED
90
+    private $scopedErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED
91
+    private $tracedErrors = 0x77FB; // E_ALL - E_STRICT - E_PARSE
92
+    private $screamedErrors = 0x55; // E_ERROR + E_CORE_ERROR + E_COMPILE_ERROR + E_PARSE
93
+    private $loggedErrors = 0;
94
+    private $traceReflector;
95
+
96
+    private $isRecursive = 0;
97
+    private $isRoot = false;
98
+    private $exceptionHandler;
99
+    private $bootstrappingLogger;
100
+
101
+    private static $reservedMemory;
102
+    private static $toStringException = null;
103
+    private static $silencedErrorCache = [];
104
+    private static $silencedErrorCount = 0;
105
+    private static $exitCode = 0;
106
+
107
+    /**
108
+     * Registers the error handler.
109
+     *
110
+     * @param self|null $handler The handler to register
111
+     * @param bool      $replace Whether to replace or not any existing handler
112
+     *
113
+     * @return self The registered error handler
114
+     */
115
+    public static function register(self $handler = null, $replace = true)
116
+    {
117
+        if (null === self::$reservedMemory) {
118
+            self::$reservedMemory = str_repeat('x', 10240);
119
+            register_shutdown_function(__CLASS__.'::handleFatalError');
120
+        }
121
+
122
+        if ($handlerIsNew = null === $handler) {
123
+            $handler = new static();
124
+        }
125
+
126
+        if (null === $prev = set_error_handler([$handler, 'handleError'])) {
127
+            restore_error_handler();
128
+            // Specifying the error types earlier would expose us to https://bugs.php.net/63206
129
+            set_error_handler([$handler, 'handleError'], $handler->thrownErrors | $handler->loggedErrors);
130
+            $handler->isRoot = true;
131
+        }
132
+
133
+        if ($handlerIsNew && \is_array($prev) && $prev[0] instanceof self) {
134
+            $handler = $prev[0];
135
+            $replace = false;
136
+        }
137
+        if (!$replace && $prev) {
138
+            restore_error_handler();
139
+            $handlerIsRegistered = \is_array($prev) && $handler === $prev[0];
140
+        } else {
141
+            $handlerIsRegistered = true;
142
+        }
143
+        if (\is_array($prev = set_exception_handler([$handler, 'handleException'])) && $prev[0] instanceof self) {
144
+            restore_exception_handler();
145
+            if (!$handlerIsRegistered) {
146
+                $handler = $prev[0];
147
+            } elseif ($handler !== $prev[0] && $replace) {
148
+                set_exception_handler([$handler, 'handleException']);
149
+                $p = $prev[0]->setExceptionHandler(null);
150
+                $handler->setExceptionHandler($p);
151
+                $prev[0]->setExceptionHandler($p);
152
+            }
153
+        } else {
154
+            $handler->setExceptionHandler($prev);
155
+        }
156
+
157
+        $handler->throwAt(E_ALL & $handler->thrownErrors, true);
158
+
159
+        return $handler;
160
+    }
161
+
162
+    public function __construct(BufferingLogger $bootstrappingLogger = null)
163
+    {
164
+        if ($bootstrappingLogger) {
165
+            $this->bootstrappingLogger = $bootstrappingLogger;
166
+            $this->setDefaultLogger($bootstrappingLogger);
167
+        }
168
+        $this->traceReflector = new \ReflectionProperty('Exception', 'trace');
169
+        $this->traceReflector->setAccessible(true);
170
+    }
171
+
172
+    /**
173
+     * Sets a logger to non assigned errors levels.
174
+     *
175
+     * @param LoggerInterface $logger  A PSR-3 logger to put as default for the given levels
176
+     * @param array|int       $levels  An array map of E_* to LogLevel::* or an integer bit field of E_* constants
177
+     * @param bool            $replace Whether to replace or not any existing logger
178
+     */
179
+    public function setDefaultLogger(LoggerInterface $logger, $levels = E_ALL, $replace = false)
180
+    {
181
+        $loggers = [];
182
+
183
+        if (\is_array($levels)) {
184
+            foreach ($levels as $type => $logLevel) {
185
+                if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] === $this->bootstrappingLogger) {
186
+                    $loggers[$type] = [$logger, $logLevel];
187
+                }
188
+            }
189
+        } else {
190
+            if (null === $levels) {
191
+                $levels = E_ALL;
192
+            }
193
+            foreach ($this->loggers as $type => $log) {
194
+                if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) {
195
+                    $log[0] = $logger;
196
+                    $loggers[$type] = $log;
197
+                }
198
+            }
199
+        }
200
+
201
+        $this->setLoggers($loggers);
202
+    }
203
+
204
+    /**
205
+     * Sets a logger for each error level.
206
+     *
207
+     * @param array $loggers Error levels to [LoggerInterface|null, LogLevel::*] map
208
+     *
209
+     * @return array The previous map
210
+     *
211
+     * @throws \InvalidArgumentException
212
+     */
213
+    public function setLoggers(array $loggers)
214
+    {
215
+        $prevLogged = $this->loggedErrors;
216
+        $prev = $this->loggers;
217
+        $flush = [];
218
+
219
+        foreach ($loggers as $type => $log) {
220
+            if (!isset($prev[$type])) {
221
+                throw new \InvalidArgumentException('Unknown error type: '.$type);
222
+            }
223
+            if (!\is_array($log)) {
224
+                $log = [$log];
225
+            } elseif (!\array_key_exists(0, $log)) {
226
+                throw new \InvalidArgumentException('No logger provided');
227
+            }
228
+            if (null === $log[0]) {
229
+                $this->loggedErrors &= ~$type;
230
+            } elseif ($log[0] instanceof LoggerInterface) {
231
+                $this->loggedErrors |= $type;
232
+            } else {
233
+                throw new \InvalidArgumentException('Invalid logger provided');
234
+            }
235
+            $this->loggers[$type] = $log + $prev[$type];
236
+
237
+            if ($this->bootstrappingLogger && $prev[$type][0] === $this->bootstrappingLogger) {
238
+                $flush[$type] = $type;
239
+            }
240
+        }
241
+        $this->reRegister($prevLogged | $this->thrownErrors);
242
+
243
+        if ($flush) {
244
+            foreach ($this->bootstrappingLogger->cleanLogs() as $log) {
245
+                $type = $log[2]['exception'] instanceof \ErrorException ? $log[2]['exception']->getSeverity() : E_ERROR;
246
+                if (!isset($flush[$type])) {
247
+                    $this->bootstrappingLogger->log($log[0], $log[1], $log[2]);
248
+                } elseif ($this->loggers[$type][0]) {
249
+                    $this->loggers[$type][0]->log($this->loggers[$type][1], $log[1], $log[2]);
250
+                }
251
+            }
252
+        }
253
+
254
+        return $prev;
255
+    }
256
+
257
+    /**
258
+     * Sets a user exception handler.
259
+     *
260
+     * @param callable $handler A handler that will be called on Exception
261
+     *
262
+     * @return callable|null The previous exception handler
263
+     */
264
+    public function setExceptionHandler(callable $handler = null)
265
+    {
266
+        $prev = $this->exceptionHandler;
267
+        $this->exceptionHandler = $handler;
268
+
269
+        return $prev;
270
+    }
271
+
272
+    /**
273
+     * Sets the PHP error levels that throw an exception when a PHP error occurs.
274
+     *
275
+     * @param int  $levels  A bit field of E_* constants for thrown errors
276
+     * @param bool $replace Replace or amend the previous value
277
+     *
278
+     * @return int The previous value
279
+     */
280
+    public function throwAt($levels, $replace = false)
281
+    {
282
+        $prev = $this->thrownErrors;
283
+        $this->thrownErrors = ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED;
284
+        if (!$replace) {
285
+            $this->thrownErrors |= $prev;
286
+        }
287
+        $this->reRegister($prev | $this->loggedErrors);
288
+
289
+        return $prev;
290
+    }
291
+
292
+    /**
293
+     * Sets the PHP error levels for which local variables are preserved.
294
+     *
295
+     * @param int  $levels  A bit field of E_* constants for scoped errors
296
+     * @param bool $replace Replace or amend the previous value
297
+     *
298
+     * @return int The previous value
299
+     */
300
+    public function scopeAt($levels, $replace = false)
301
+    {
302
+        $prev = $this->scopedErrors;
303
+        $this->scopedErrors = (int) $levels;
304
+        if (!$replace) {
305
+            $this->scopedErrors |= $prev;
306
+        }
307
+
308
+        return $prev;
309
+    }
310
+
311
+    /**
312
+     * Sets the PHP error levels for which the stack trace is preserved.
313
+     *
314
+     * @param int  $levels  A bit field of E_* constants for traced errors
315
+     * @param bool $replace Replace or amend the previous value
316
+     *
317
+     * @return int The previous value
318
+     */
319
+    public function traceAt($levels, $replace = false)
320
+    {
321
+        $prev = $this->tracedErrors;
322
+        $this->tracedErrors = (int) $levels;
323
+        if (!$replace) {
324
+            $this->tracedErrors |= $prev;
325
+        }
326
+
327
+        return $prev;
328
+    }
329
+
330
+    /**
331
+     * Sets the error levels where the @-operator is ignored.
332
+     *
333
+     * @param int  $levels  A bit field of E_* constants for screamed errors
334
+     * @param bool $replace Replace or amend the previous value
335
+     *
336
+     * @return int The previous value
337
+     */
338
+    public function screamAt($levels, $replace = false)
339
+    {
340
+        $prev = $this->screamedErrors;
341
+        $this->screamedErrors = (int) $levels;
342
+        if (!$replace) {
343
+            $this->screamedErrors |= $prev;
344
+        }
345
+
346
+        return $prev;
347
+    }
348
+
349
+    /**
350
+     * Re-registers as a PHP error handler if levels changed.
351
+     */
352
+    private function reRegister($prev)
353
+    {
354
+        if ($prev !== $this->thrownErrors | $this->loggedErrors) {
355
+            $handler = set_error_handler('var_dump');
356
+            $handler = \is_array($handler) ? $handler[0] : null;
357
+            restore_error_handler();
358
+            if ($handler === $this) {
359
+                restore_error_handler();
360
+                if ($this->isRoot) {
361
+                    set_error_handler([$this, 'handleError'], $this->thrownErrors | $this->loggedErrors);
362
+                } else {
363
+                    set_error_handler([$this, 'handleError']);
364
+                }
365
+            }
366
+        }
367
+    }
368
+
369
+    /**
370
+     * Handles errors by filtering then logging them according to the configured bit fields.
371
+     *
372
+     * @param int    $type    One of the E_* constants
373
+     * @param string $message
374
+     * @param string $file
375
+     * @param int    $line
376
+     *
377
+     * @return bool Returns false when no handling happens so that the PHP engine can handle the error itself
378
+     *
379
+     * @throws \ErrorException When $this->thrownErrors requests so
380
+     *
381
+     * @internal
382
+     */
383
+    public function handleError($type, $message, $file, $line)
384
+    {
385
+        // @deprecated to be removed in Symfony 5.0
386
+        if (\PHP_VERSION_ID >= 70300 && $message && '"' === $message[0] && 0 === strpos($message, '"continue') && preg_match('/^"continue(?: \d++)?" targeting switch is equivalent to "break(?: \d++)?"\. Did you mean to use "continue(?: \d++)?"\?$/', $message)) {
387
+            $type = E_DEPRECATED;
388
+        }
389
+
390
+        // Level is the current error reporting level to manage silent error.
391
+        $level = error_reporting();
392
+        $silenced = 0 === ($level & $type);
393
+        // Strong errors are not authorized to be silenced.
394
+        $level |= E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED;
395
+        $log = $this->loggedErrors & $type;
396
+        $throw = $this->thrownErrors & $type & $level;
397
+        $type &= $level | $this->screamedErrors;
398
+
399
+        if (!$type || (!$log && !$throw)) {
400
+            return !$silenced && $type && $log;
401
+        }
402
+        $scope = $this->scopedErrors & $type;
403
+
404
+        if (4 < $numArgs = \func_num_args()) {
405
+            $context = $scope ? (func_get_arg(4) ?: []) : [];
406
+        } else {
407
+            $context = [];
408
+        }
409
+
410
+        if (isset($context['GLOBALS']) && $scope) {
411
+            $e = $context;                  // Whatever the signature of the method,
412
+            unset($e['GLOBALS'], $context); // $context is always a reference in 5.3
413
+            $context = $e;
414
+        }
415
+
416
+        if (false !== strpos($message, "class@anonymous\0")) {
417
+            $logMessage = $this->levels[$type].': '.(new FlattenException())->setMessage($message)->getMessage();
418
+        } else {
419
+            $logMessage = $this->levels[$type].': '.$message;
420
+        }
421
+
422
+        if (null !== self::$toStringException) {
423
+            $errorAsException = self::$toStringException;
424
+            self::$toStringException = null;
425
+        } elseif (!$throw && !($type & $level)) {
426
+            if (!isset(self::$silencedErrorCache[$id = $file.':'.$line])) {
427
+                $lightTrace = $this->tracedErrors & $type ? $this->cleanTrace(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5), $type, $file, $line, false) : [];
428
+                $errorAsException = new SilencedErrorContext($type, $file, $line, isset($lightTrace[1]) ? [$lightTrace[0]] : $lightTrace);
429
+            } elseif (isset(self::$silencedErrorCache[$id][$message])) {
430
+                $lightTrace = null;
431
+                $errorAsException = self::$silencedErrorCache[$id][$message];
432
+                ++$errorAsException->count;
433
+            } else {
434
+                $lightTrace = [];
435
+                $errorAsException = null;
436
+            }
437
+
438
+            if (100 < ++self::$silencedErrorCount) {
439
+                self::$silencedErrorCache = $lightTrace = [];
440
+                self::$silencedErrorCount = 1;
441
+            }
442
+            if ($errorAsException) {
443
+                self::$silencedErrorCache[$id][$message] = $errorAsException;
444
+            }
445
+            if (null === $lightTrace) {
446
+                return;
447
+            }
448
+        } else {
449
+            $errorAsException = new \ErrorException($logMessage, 0, $type, $file, $line);
450
+
451
+            if ($throw || $this->tracedErrors & $type) {
452
+                $backtrace = $errorAsException->getTrace();
453
+                $lightTrace = $this->cleanTrace($backtrace, $type, $file, $line, $throw);
454
+                $this->traceReflector->setValue($errorAsException, $lightTrace);
455
+            } else {
456
+                $this->traceReflector->setValue($errorAsException, []);
457
+                $backtrace = [];
458
+            }
459
+        }
460
+
461
+        if ($throw) {
462
+            if (E_USER_ERROR & $type) {
463
+                for ($i = 1; isset($backtrace[$i]); ++$i) {
464
+                    if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function'])
465
+                        && '__toString' === $backtrace[$i]['function']
466
+                        && '->' === $backtrace[$i]['type']
467
+                        && !isset($backtrace[$i - 1]['class'])
468
+                        && ('trigger_error' === $backtrace[$i - 1]['function'] || 'user_error' === $backtrace[$i - 1]['function'])
469
+                    ) {
470
+                        // Here, we know trigger_error() has been called from __toString().
471
+                        // PHP triggers a fatal error when throwing from __toString().
472
+                        // A small convention allows working around the limitation:
473
+                        // given a caught $e exception in __toString(), quitting the method with
474
+                        // `return trigger_error($e, E_USER_ERROR);` allows this error handler
475
+                        // to make $e get through the __toString() barrier.
476
+
477
+                        foreach ($context as $e) {
478
+                            if ($e instanceof \Throwable && $e->__toString() === $message) {
479
+                                self::$toStringException = $e;
480
+
481
+                                return true;
482
+                            }
483
+                        }
484
+
485
+                        // Display the original error message instead of the default one.
486
+                        $this->handleException($errorAsException);
487
+
488
+                        // Stop the process by giving back the error to the native handler.
489
+                        return false;
490
+                    }
491
+                }
492
+            }
493
+
494
+            throw $errorAsException;
495
+        }
496
+
497
+        if ($this->isRecursive) {
498
+            $log = 0;
499
+        } else {
500
+            if (!\defined('HHVM_VERSION')) {
501
+                $currentErrorHandler = set_error_handler('var_dump');
502
+                restore_error_handler();
503
+            }
504
+
505
+            try {
506
+                $this->isRecursive = true;
507
+                $level = ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG;
508
+                $this->loggers[$type][0]->log($level, $logMessage, $errorAsException ? ['exception' => $errorAsException] : []);
509
+            } finally {
510
+                $this->isRecursive = false;
511
+
512
+                if (!\defined('HHVM_VERSION')) {
513
+                    set_error_handler($currentErrorHandler);
514
+                }
515
+            }
516
+        }
517
+
518
+        return !$silenced && $type && $log;
519
+    }
520
+
521
+    /**
522
+     * Handles an exception by logging then forwarding it to another handler.
523
+     *
524
+     * @param \Exception|\Throwable $exception An exception to handle
525
+     * @param array                 $error     An array as returned by error_get_last()
526
+     *
527
+     * @internal
528
+     */
529
+    public function handleException($exception, array $error = null)
530
+    {
531
+        if (null === $error) {
532
+            self::$exitCode = 255;
533
+        }
534
+        if (!$exception instanceof \Exception) {
535
+            $exception = new FatalThrowableError($exception);
536
+        }
537
+        $type = $exception instanceof FatalErrorException ? $exception->getSeverity() : E_ERROR;
538
+        $handlerException = null;
539
+
540
+        if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) {
541
+            if (false !== strpos($message = $exception->getMessage(), "class@anonymous\0")) {
542
+                $message = (new FlattenException())->setMessage($message)->getMessage();
543
+            }
544
+            if ($exception instanceof FatalErrorException) {
545
+                if ($exception instanceof FatalThrowableError) {
546
+                    $error = [
547
+                        'type' => $type,
548
+                        'message' => $message,
549
+                        'file' => $exception->getFile(),
550
+                        'line' => $exception->getLine(),
551
+                    ];
552
+                } else {
553
+                    $message = 'Fatal '.$message;
554
+                }
555
+            } elseif ($exception instanceof \ErrorException) {
556
+                $message = 'Uncaught '.$message;
557
+            } else {
558
+                $message = 'Uncaught Exception: '.$message;
559
+            }
560
+        }
561
+        if ($this->loggedErrors & $type) {
562
+            try {
563
+                $this->loggers[$type][0]->log($this->loggers[$type][1], $message, ['exception' => $exception]);
564
+            } catch (\Throwable $handlerException) {
565
+            }
566
+        }
567
+        if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) {
568
+            foreach ($this->getFatalErrorHandlers() as $handler) {
569
+                if ($e = $handler->handleError($error, $exception)) {
570
+                    $exception = $e;
571
+                    break;
572
+                }
573
+            }
574
+        }
575
+        $exceptionHandler = $this->exceptionHandler;
576
+        $this->exceptionHandler = null;
577
+        try {
578
+            if (null !== $exceptionHandler) {
579
+                return $exceptionHandler($exception);
580
+            }
581
+            $handlerException = $handlerException ?: $exception;
582
+        } catch (\Throwable $handlerException) {
583
+        }
584
+        if ($exception === $handlerException) {
585
+            self::$reservedMemory = null; // Disable the fatal error handler
586
+            throw $exception; // Give back $exception to the native handler
587
+        }
588
+        $this->handleException($handlerException);
589
+    }
590
+
591
+    /**
592
+     * Shutdown registered function for handling PHP fatal errors.
593
+     *
594
+     * @param array $error An array as returned by error_get_last()
595
+     *
596
+     * @internal
597
+     */
598
+    public static function handleFatalError(array $error = null)
599
+    {
600
+        if (null === self::$reservedMemory) {
601
+            return;
602
+        }
603
+
604
+        $handler = self::$reservedMemory = null;
605
+        $handlers = [];
606
+        $previousHandler = null;
607
+        $sameHandlerLimit = 10;
608
+
609
+        while (!\is_array($handler) || !$handler[0] instanceof self) {
610
+            $handler = set_exception_handler('var_dump');
611
+            restore_exception_handler();
612
+
613
+            if (!$handler) {
614
+                break;
615
+            }
616
+            restore_exception_handler();
617
+
618
+            if ($handler !== $previousHandler) {
619
+                array_unshift($handlers, $handler);
620
+                $previousHandler = $handler;
621
+            } elseif (0 === --$sameHandlerLimit) {
622
+                $handler = null;
623
+                break;
624
+            }
625
+        }
626
+        foreach ($handlers as $h) {
627
+            set_exception_handler($h);
628
+        }
629
+        if (!$handler) {
630
+            return;
631
+        }
632
+        if ($handler !== $h) {
633
+            $handler[0]->setExceptionHandler($h);
634
+        }
635
+        $handler = $handler[0];
636
+        $handlers = [];
637
+
638
+        if ($exit = null === $error) {
639
+            $error = error_get_last();
640
+        }
641
+
642
+        if ($error && $error['type'] &= E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR) {
643
+            // Let's not throw anymore but keep logging
644
+            $handler->throwAt(0, true);
645
+            $trace = isset($error['backtrace']) ? $error['backtrace'] : null;
646
+
647
+            if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) {
648
+                $exception = new OutOfMemoryException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, false, $trace);
649
+            } else {
650
+                $exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true, $trace);
651
+            }
652
+        } else {
653
+            $exception = null;
654
+        }
655
+
656
+        try {
657
+            if (null !== $exception) {
658
+                self::$exitCode = 255;
659
+                $handler->handleException($exception, $error);
660
+            }
661
+        } catch (FatalErrorException $e) {
662
+            // Ignore this re-throw
663
+        }
664
+
665
+        if ($exit && self::$exitCode) {
666
+            $exitCode = self::$exitCode;
667
+            register_shutdown_function('register_shutdown_function', function () use ($exitCode) { exit($exitCode); });
668
+        }
669
+    }
670
+
671
+    /**
672
+     * Gets the fatal error handlers.
673
+     *
674
+     * Override this method if you want to define more fatal error handlers.
675
+     *
676
+     * @return FatalErrorHandlerInterface[] An array of FatalErrorHandlerInterface
677
+     */
678
+    protected function getFatalErrorHandlers()
679
+    {
680
+        return [
681
+            new UndefinedFunctionFatalErrorHandler(),
682
+            new UndefinedMethodFatalErrorHandler(),
683
+            new ClassNotFoundFatalErrorHandler(),
684
+        ];
685
+    }
686
+
687
+    /**
688
+     * Cleans the trace by removing function arguments and the frames added by the error handler and DebugClassLoader.
689
+     */
690
+    private function cleanTrace($backtrace, $type, $file, $line, $throw)
691
+    {
692
+        $lightTrace = $backtrace;
693
+
694
+        for ($i = 0; isset($backtrace[$i]); ++$i) {
695
+            if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) {
696
+                $lightTrace = \array_slice($lightTrace, 1 + $i);
697
+                break;
698
+            }
699
+        }
700
+        if (class_exists(DebugClassLoader::class, false)) {
701
+            for ($i = \count($lightTrace) - 2; 0 < $i; --$i) {
702
+                if (DebugClassLoader::class === ($lightTrace[$i]['class'] ?? null)) {
703
+                    array_splice($lightTrace, --$i, 2);
704
+                }
705
+            }
706
+        }
707
+        if (!($throw || $this->scopedErrors & $type)) {
708
+            for ($i = 0; isset($lightTrace[$i]); ++$i) {
709
+                unset($lightTrace[$i]['args'], $lightTrace[$i]['object']);
710
+            }
711
+        }
712
+
713
+        return $lightTrace;
714
+    }
715
+}