Verzeichnisstruktur phpBB-3.2.0
- Veröffentlicht
- 06.01.2017
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
ErrorHandler.php
001 <?php
002
003 /*
004 * This file is part of the Symfony package.
005 *
006 * (c) Fabien Potencier <fabien@symfony.com>
007 *
008 * For the full copyright and license information, please view the LICENSE
009 * file that was distributed with this source code.
010 */
011
012 namespace Symfony\Component\Debug;
013
014 use Psr\Log\LogLevel;
015 use Psr\Log\LoggerInterface;
016 use Symfony\Component\Debug\Exception\ContextErrorException;
017 use Symfony\Component\Debug\Exception\FatalErrorException;
018 use Symfony\Component\Debug\Exception\FatalThrowableError;
019 use Symfony\Component\Debug\Exception\OutOfMemoryException;
020 use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler;
021 use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
022 use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
023 use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface;
024
025 /**
026 * A generic ErrorHandler for the PHP engine.
027 *
028 * Provides five bit fields that control how errors are handled:
029 * - thrownErrors: errors thrown as \ErrorException
030 * - loggedErrors: logged errors, when not @-silenced
031 * - scopedErrors: errors thrown or logged with their local context
032 * - tracedErrors: errors logged with their stack trace, only once for repeated errors
033 * - screamedErrors: never @-silenced errors
034 *
035 * Each error level can be logged by a dedicated PSR-3 logger object.
036 * Screaming only applies to logging.
037 * Throwing takes precedence over logging.
038 * Uncaught exceptions are logged as E_ERROR.
039 * E_DEPRECATED and E_USER_DEPRECATED levels never throw.
040 * E_RECOVERABLE_ERROR and E_USER_ERROR levels always throw.
041 * Non catchable errors that can be detected at shutdown time are logged when the scream bit field allows so.
042 * As errors have a performance cost, repeated errors are all logged, so that the developer
043 * can see them and weight them as more important to fix than others of the same level.
044 *
045 * @author Nicolas Grekas <p@tchwork.com>
046 */
047 class ErrorHandler
048 {
049 /**
050 * @deprecated since version 2.6, to be removed in 3.0.
051 */
052 const TYPE_DEPRECATION = -100;
053
054 private $levels = array(
055 E_DEPRECATED => 'Deprecated',
056 E_USER_DEPRECATED => 'User Deprecated',
057 E_NOTICE => 'Notice',
058 E_USER_NOTICE => 'User Notice',
059 E_STRICT => 'Runtime Notice',
060 E_WARNING => 'Warning',
061 E_USER_WARNING => 'User Warning',
062 E_COMPILE_WARNING => 'Compile Warning',
063 E_CORE_WARNING => 'Core Warning',
064 E_USER_ERROR => 'User Error',
065 E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
066 E_COMPILE_ERROR => 'Compile Error',
067 E_PARSE => 'Parse Error',
068 E_ERROR => 'Error',
069 E_CORE_ERROR => 'Core Error',
070 );
071
072 private $loggers = array(
073 E_DEPRECATED => array(null, LogLevel::INFO),
074 E_USER_DEPRECATED => array(null, LogLevel::INFO),
075 E_NOTICE => array(null, LogLevel::WARNING),
076 E_USER_NOTICE => array(null, LogLevel::WARNING),
077 E_STRICT => array(null, LogLevel::WARNING),
078 E_WARNING => array(null, LogLevel::WARNING),
079 E_USER_WARNING => array(null, LogLevel::WARNING),
080 E_COMPILE_WARNING => array(null, LogLevel::WARNING),
081 E_CORE_WARNING => array(null, LogLevel::WARNING),
082 E_USER_ERROR => array(null, LogLevel::CRITICAL),
083 E_RECOVERABLE_ERROR => array(null, LogLevel::CRITICAL),
084 E_COMPILE_ERROR => array(null, LogLevel::CRITICAL),
085 E_PARSE => array(null, LogLevel::CRITICAL),
086 E_ERROR => array(null, LogLevel::CRITICAL),
087 E_CORE_ERROR => array(null, LogLevel::CRITICAL),
088 );
089
090 private $thrownErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED
091 private $scopedErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED
092 private $tracedErrors = 0x77FB; // E_ALL - E_STRICT - E_PARSE
093 private $screamedErrors = 0x55; // E_ERROR + E_CORE_ERROR + E_COMPILE_ERROR + E_PARSE
094 private $loggedErrors = 0;
095
096 private $loggedTraces = array();
097 private $isRecursive = 0;
098 private $isRoot = false;
099 private $exceptionHandler;
100 private $bootstrappingLogger;
101
102 private static $reservedMemory;
103 private static $stackedErrors = array();
104 private static $stackedErrorLevels = array();
105 private static $toStringException = null;
106
107 /**
108 * Same init value as thrownErrors.
109 *
110 * @deprecated since version 2.6, to be removed in 3.0.
111 */
112 private $displayErrors = 0x1FFF;
113
114 /**
115 * Registers the error handler.
116 *
117 * @param self|null|int $handler The handler to register, or @deprecated (since version 2.6, to be removed in 3.0) bit field of thrown levels
118 * @param bool $replace Whether to replace or not any existing handler
119 *
120 * @return self The registered error handler
121 */
122 public static function register($handler = null, $replace = true)
123 {
124 if (null === self::$reservedMemory) {
125 self::$reservedMemory = str_repeat('x', 10240);
126 register_shutdown_function(__CLASS__.'::handleFatalError');
127 }
128
129 $levels = -1;
130
131 if ($handlerIsNew = !$handler instanceof self) {
132 // @deprecated polymorphism, to be removed in 3.0
133 if (null !== $handler) {
134 $levels = $replace ? $handler : 0;
135 $replace = true;
136 }
137 $handler = new static();
138 }
139
140 if (null === $prev = set_error_handler(array($handler, 'handleError'))) {
141 restore_error_handler();
142 // Specifying the error types earlier would expose us to https://bugs.php.net/63206
143 set_error_handler(array($handler, 'handleError'), $handler->thrownErrors | $handler->loggedErrors);
144 $handler->isRoot = true;
145 }
146
147 if ($handlerIsNew && is_array($prev) && $prev[0] instanceof self) {
148 $handler = $prev[0];
149 $replace = false;
150 }
151 if ($replace || !$prev) {
152 $handler->setExceptionHandler(set_exception_handler(array($handler, 'handleException')));
153 } else {
154 restore_error_handler();
155 }
156
157 $handler->throwAt($levels & $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 }
169
170 /**
171 * Sets a logger to non assigned errors levels.
172 *
173 * @param LoggerInterface $logger A PSR-3 logger to put as default for the given levels
174 * @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants
175 * @param bool $replace Whether to replace or not any existing logger
176 */
177 public function setDefaultLogger(LoggerInterface $logger, $levels = null, $replace = false)
178 {
179 $loggers = array();
180
181 if (is_array($levels)) {
182 foreach ($levels as $type => $logLevel) {
183 if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] === $this->bootstrappingLogger) {
184 $loggers[$type] = array($logger, $logLevel);
185 }
186 }
187 } else {
188 if (null === $levels) {
189 $levels = E_ALL | E_STRICT;
190 }
191 foreach ($this->loggers as $type => $log) {
192 if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) {
193 $log[0] = $logger;
194 $loggers[$type] = $log;
195 }
196 }
197 }
198
199 $this->setLoggers($loggers);
200 }
201
202 /**
203 * Sets a logger for each error level.
204 *
205 * @param array $loggers Error levels to [LoggerInterface|null, LogLevel::*] map
206 *
207 * @return array The previous map
208 *
209 * @throws \InvalidArgumentException
210 */
211 public function setLoggers(array $loggers)
212 {
213 $prevLogged = $this->loggedErrors;
214 $prev = $this->loggers;
215 $flush = array();
216
217 foreach ($loggers as $type => $log) {
218 if (!isset($prev[$type])) {
219 throw new \InvalidArgumentException('Unknown error type: '.$type);
220 }
221 if (!is_array($log)) {
222 $log = array($log);
223 } elseif (!array_key_exists(0, $log)) {
224 throw new \InvalidArgumentException('No logger provided');
225 }
226 if (null === $log[0]) {
227 $this->loggedErrors &= ~$type;
228 } elseif ($log[0] instanceof LoggerInterface) {
229 $this->loggedErrors |= $type;
230 } else {
231 throw new \InvalidArgumentException('Invalid logger provided');
232 }
233 $this->loggers[$type] = $log + $prev[$type];
234
235 if ($this->bootstrappingLogger && $prev[$type][0] === $this->bootstrappingLogger) {
236 $flush[$type] = $type;
237 }
238 }
239 $this->reRegister($prevLogged | $this->thrownErrors);
240
241 if ($flush) {
242 foreach ($this->bootstrappingLogger->cleanLogs() as $log) {
243 $type = $log[2]['type'];
244 if (!isset($flush[$type])) {
245 $this->bootstrappingLogger->log($log[0], $log[1], $log[2]);
246 } elseif ($this->loggers[$type][0]) {
247 $this->loggers[$type][0]->log($this->loggers[$type][1], $log[1], $log[2]);
248 }
249 }
250 }
251
252 return $prev;
253 }
254
255 /**
256 * Sets a user exception handler.
257 *
258 * @param callable $handler A handler that will be called on Exception
259 *
260 * @return callable|null The previous exception handler
261 *
262 * @throws \InvalidArgumentException
263 */
264 public function setExceptionHandler($handler)
265 {
266 if (null !== $handler && !is_callable($handler)) {
267 throw new \LogicException('The exception handler must be a valid PHP callable.');
268 }
269 $prev = $this->exceptionHandler;
270 $this->exceptionHandler = $handler;
271
272 return $prev;
273 }
274
275 /**
276 * Sets the PHP error levels that throw an exception when a PHP error occurs.
277 *
278 * @param int $levels A bit field of E_* constants for thrown errors
279 * @param bool $replace Replace or amend the previous value
280 *
281 * @return int The previous value
282 */
283 public function throwAt($levels, $replace = false)
284 {
285 $prev = $this->thrownErrors;
286 $this->thrownErrors = ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED;
287 if (!$replace) {
288 $this->thrownErrors |= $prev;
289 }
290 $this->reRegister($prev | $this->loggedErrors);
291
292 // $this->displayErrors is @deprecated since version 2.6
293 $this->displayErrors = $this->thrownErrors;
294
295 return $prev;
296 }
297
298 /**
299 * Sets the PHP error levels for which local variables are preserved.
300 *
301 * @param int $levels A bit field of E_* constants for scoped errors
302 * @param bool $replace Replace or amend the previous value
303 *
304 * @return int The previous value
305 */
306 public function scopeAt($levels, $replace = false)
307 {
308 $prev = $this->scopedErrors;
309 $this->scopedErrors = (int) $levels;
310 if (!$replace) {
311 $this->scopedErrors |= $prev;
312 }
313
314 return $prev;
315 }
316
317 /**
318 * Sets the PHP error levels for which the stack trace is preserved.
319 *
320 * @param int $levels A bit field of E_* constants for traced errors
321 * @param bool $replace Replace or amend the previous value
322 *
323 * @return int The previous value
324 */
325 public function traceAt($levels, $replace = false)
326 {
327 $prev = $this->tracedErrors;
328 $this->tracedErrors = (int) $levels;
329 if (!$replace) {
330 $this->tracedErrors |= $prev;
331 }
332
333 return $prev;
334 }
335
336 /**
337 * Sets the error levels where the @-operator is ignored.
338 *
339 * @param int $levels A bit field of E_* constants for screamed errors
340 * @param bool $replace Replace or amend the previous value
341 *
342 * @return int The previous value
343 */
344 public function screamAt($levels, $replace = false)
345 {
346 $prev = $this->screamedErrors;
347 $this->screamedErrors = (int) $levels;
348 if (!$replace) {
349 $this->screamedErrors |= $prev;
350 }
351
352 return $prev;
353 }
354
355 /**
356 * Re-registers as a PHP error handler if levels changed.
357 */
358 private function reRegister($prev)
359 {
360 if ($prev !== $this->thrownErrors | $this->loggedErrors) {
361 $handler = set_error_handler('var_dump');
362 $handler = is_array($handler) ? $handler[0] : null;
363 restore_error_handler();
364 if ($handler === $this) {
365 restore_error_handler();
366 if ($this->isRoot) {
367 set_error_handler(array($this, 'handleError'), $this->thrownErrors | $this->loggedErrors);
368 } else {
369 set_error_handler(array($this, 'handleError'));
370 }
371 }
372 }
373 }
374
375 /**
376 * Handles errors by filtering then logging them according to the configured bit fields.
377 *
378 * @param int $type One of the E_* constants
379 * @param string $message
380 * @param string $file
381 * @param int $line
382 * @param array $context
383 * @param array $backtrace
384 *
385 * @return bool Returns false when no handling happens so that the PHP engine can handle the error itself
386 *
387 * @throws \ErrorException When $this->thrownErrors requests so
388 *
389 * @internal
390 */
391 public function handleError($type, $message, $file, $line, array $context, array $backtrace = null)
392 {
393 $level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED;
394 $log = $this->loggedErrors & $type;
395 $throw = $this->thrownErrors & $type & $level;
396 $type &= $level | $this->screamedErrors;
397
398 if (!$type || (!$log && !$throw)) {
399 return $type && $log;
400 }
401
402 if (isset($context['GLOBALS']) && ($this->scopedErrors & $type)) {
403 $e = $context; // Whatever the signature of the method,
404 unset($e['GLOBALS'], $context); // $context is always a reference in 5.3
405 $context = $e;
406 }
407
408 if (null !== $backtrace && $type & E_ERROR) {
409 // E_ERROR fatal errors are triggered on HHVM when
410 // hhvm.error_handling.call_user_handler_on_fatals=1
411 // which is the way to get their backtrace.
412 $this->handleFatalError(compact('type', 'message', 'file', 'line', 'backtrace'));
413
414 return true;
415 }
416
417 if ($throw) {
418 if (null !== self::$toStringException) {
419 $throw = self::$toStringException;
420 self::$toStringException = null;
421 } elseif (($this->scopedErrors & $type) && class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) {
422 // Checking for class existence is a work around for https://bugs.php.net/42098
423 $throw = new ContextErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line, $context);
424 } else {
425 $throw = new \ErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line);
426 }
427
428 if (PHP_VERSION_ID <= 50407 && (PHP_VERSION_ID >= 50400 || PHP_VERSION_ID <= 50317)) {
429 // Exceptions thrown from error handlers are sometimes not caught by the exception
430 // handler and shutdown handlers are bypassed before 5.4.8/5.3.18.
431 // We temporarily re-enable display_errors to prevent any blank page related to this bug.
432
433 $throw->errorHandlerCanary = new ErrorHandlerCanary();
434 }
435
436 if (E_USER_ERROR & $type) {
437 $backtrace = $backtrace ?: $throw->getTrace();
438
439 for ($i = 1; isset($backtrace[$i]); ++$i) {
440 if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function'])
441 && '__toString' === $backtrace[$i]['function']
442 && '->' === $backtrace[$i]['type']
443 && !isset($backtrace[$i - 1]['class'])
444 && ('trigger_error' === $backtrace[$i - 1]['function'] || 'user_error' === $backtrace[$i - 1]['function'])
445 ) {
446 // Here, we know trigger_error() has been called from __toString().
447 // HHVM is fine with throwing from __toString() but PHP triggers a fatal error instead.
448 // A small convention allows working around the limitation:
449 // given a caught $e exception in __toString(), quitting the method with
450 // `return trigger_error($e, E_USER_ERROR);` allows this error handler
451 // to make $e get through the __toString() barrier.
452
453 foreach ($context as $e) {
454 if (($e instanceof \Exception || $e instanceof \Throwable) && $e->__toString() === $message) {
455 if (1 === $i) {
456 // On HHVM
457 $throw = $e;
458 break;
459 }
460 self::$toStringException = $e;
461
462 return true;
463 }
464 }
465
466 if (1 < $i) {
467 // On PHP (not on HHVM), display the original error message instead of the default one.
468 $this->handleException($throw);
469
470 // Stop the process by giving back the error to the native handler.
471 return false;
472 }
473 }
474 }
475 }
476
477 throw $throw;
478 }
479
480 // For duplicated errors, log the trace only once
481 $e = md5("{$type}/{$line}/{$file}\x00{$message}", true);
482 $trace = true;
483
484 if (!($this->tracedErrors & $type) || isset($this->loggedTraces[$e])) {
485 $trace = false;
486 } else {
487 $this->loggedTraces[$e] = 1;
488 }
489
490 $e = compact('type', 'file', 'line', 'level');
491
492 if ($type & $level) {
493 if ($this->scopedErrors & $type) {
494 $e['scope_vars'] = $context;
495 if ($trace) {
496 $e['stack'] = $backtrace ?: debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT);
497 }
498 } elseif ($trace) {
499 if (null === $backtrace) {
500 $e['stack'] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
501 } else {
502 foreach ($backtrace as &$frame) {
503 unset($frame['args'], $frame);
504 }
505 $e['stack'] = $backtrace;
506 }
507 }
508 }
509
510 if ($this->isRecursive) {
511 $log = 0;
512 } elseif (self::$stackedErrorLevels) {
513 self::$stackedErrors[] = array($this->loggers[$type][0], ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, $message, $e);
514 } else {
515 try {
516 $this->isRecursive = true;
517 $this->loggers[$type][0]->log(($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, $message, $e);
518 $this->isRecursive = false;
519 } catch (\Exception $e) {
520 $this->isRecursive = false;
521
522 throw $e;
523 } catch (\Throwable $e) {
524 $this->isRecursive = false;
525
526 throw $e;
527 }
528 }
529
530 return $type && $log;
531 }
532
533 /**
534 * Handles an exception by logging then forwarding it to another handler.
535 *
536 * @param \Exception|\Throwable $exception An exception to handle
537 * @param array $error An array as returned by error_get_last()
538 *
539 * @internal
540 */
541 public function handleException($exception, array $error = null)
542 {
543 if (!$exception instanceof \Exception) {
544 $exception = new FatalThrowableError($exception);
545 }
546 $type = $exception instanceof FatalErrorException ? $exception->getSeverity() : E_ERROR;
547
548 if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) {
549 $e = array(
550 'type' => $type,
551 'file' => $exception->getFile(),
552 'line' => $exception->getLine(),
553 'level' => error_reporting(),
554 'stack' => $exception->getTrace(),
555 );
556 if ($exception instanceof FatalErrorException) {
557 if ($exception instanceof FatalThrowableError) {
558 $error = array(
559 'type' => $type,
560 'message' => $message = $exception->getMessage(),
561 'file' => $e['file'],
562 'line' => $e['line'],
563 );
564 } else {
565 $message = 'Fatal '.$exception->getMessage();
566 }
567 } elseif ($exception instanceof \ErrorException) {
568 $message = 'Uncaught '.$exception->getMessage();
569 if ($exception instanceof ContextErrorException) {
570 $e['context'] = $exception->getContext();
571 }
572 } else {
573 $message = 'Uncaught Exception: '.$exception->getMessage();
574 }
575 }
576 if ($this->loggedErrors & $type) {
577 $this->loggers[$type][0]->log($this->loggers[$type][1], $message, $e);
578 }
579 if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) {
580 foreach ($this->getFatalErrorHandlers() as $handler) {
581 if ($e = $handler->handleError($error, $exception)) {
582 $exception = $e;
583 break;
584 }
585 }
586 }
587 if (empty($this->exceptionHandler)) {
588 throw $exception; // Give back $exception to the native handler
589 }
590 try {
591 call_user_func($this->exceptionHandler, $exception);
592 } catch (\Exception $handlerException) {
593 } catch (\Throwable $handlerException) {
594 }
595 if (isset($handlerException)) {
596 $this->exceptionHandler = null;
597 $this->handleException($handlerException);
598 }
599 }
600
601 /**
602 * Shutdown registered function for handling PHP fatal errors.
603 *
604 * @param array $error An array as returned by error_get_last()
605 *
606 * @internal
607 */
608 public static function handleFatalError(array $error = null)
609 {
610 if (null === self::$reservedMemory) {
611 return;
612 }
613
614 self::$reservedMemory = null;
615
616 $handler = set_error_handler('var_dump');
617 $handler = is_array($handler) ? $handler[0] : null;
618 restore_error_handler();
619
620 if (!$handler instanceof self) {
621 return;
622 }
623
624 if (null === $error) {
625 $error = error_get_last();
626 }
627
628 try {
629 while (self::$stackedErrorLevels) {
630 static::unstackErrors();
631 }
632 } catch (\Exception $exception) {
633 // Handled below
634 } catch (\Throwable $exception) {
635 // Handled below
636 }
637
638 if ($error && $error['type'] &= E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR) {
639 // Let's not throw anymore but keep logging
640 $handler->throwAt(0, true);
641 $trace = isset($error['backtrace']) ? $error['backtrace'] : null;
642
643 if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) {
644 $exception = new OutOfMemoryException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, false, $trace);
645 } else {
646 $exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true, $trace);
647 }
648 } elseif (!isset($exception)) {
649 return;
650 }
651
652 try {
653 $handler->handleException($exception, $error);
654 } catch (FatalErrorException $e) {
655 // Ignore this re-throw
656 }
657 }
658
659 /**
660 * Configures the error handler for delayed handling.
661 * Ensures also that non-catchable fatal errors are never silenced.
662 *
663 * As shown by http://bugs.php.net/42098 and http://bugs.php.net/60724
664 * PHP has a compile stage where it behaves unusually. To workaround it,
665 * we plug an error handler that only stacks errors for later.
666 *
667 * The most important feature of this is to prevent
668 * autoloading until unstackErrors() is called.
669 */
670 public static function stackErrors()
671 {
672 self::$stackedErrorLevels[] = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR);
673 }
674
675 /**
676 * Unstacks stacked errors and forwards to the logger.
677 */
678 public static function unstackErrors()
679 {
680 $level = array_pop(self::$stackedErrorLevels);
681
682 if (null !== $level) {
683 $e = error_reporting($level);
684 if ($e !== ($level | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)) {
685 // If the user changed the error level, do not overwrite it
686 error_reporting($e);
687 }
688 }
689
690 if (empty(self::$stackedErrorLevels)) {
691 $errors = self::$stackedErrors;
692 self::$stackedErrors = array();
693
694 foreach ($errors as $e) {
695 $e[0]->log($e[1], $e[2], $e[3]);
696 }
697 }
698 }
699
700 /**
701 * Gets the fatal error handlers.
702 *
703 * Override this method if you want to define more fatal error handlers.
704 *
705 * @return FatalErrorHandlerInterface[] An array of FatalErrorHandlerInterface
706 */
707 protected function getFatalErrorHandlers()
708 {
709 return array(
710 new UndefinedFunctionFatalErrorHandler(),
711 new UndefinedMethodFatalErrorHandler(),
712 new ClassNotFoundFatalErrorHandler(),
713 );
714 }
715
716 /**
717 * Sets the level at which the conversion to Exception is done.
718 *
719 * @param int|null $level The level (null to use the error_reporting() value and 0 to disable)
720 *
721 * @deprecated since version 2.6, to be removed in 3.0. Use throwAt() instead.
722 */
723 public function setLevel($level)
724 {
725 @trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the throwAt() method instead.', E_USER_DEPRECATED);
726
727 $level = null === $level ? error_reporting() : $level;
728 $this->throwAt($level, true);
729 }
730
731 /**
732 * Sets the display_errors flag value.
733 *
734 * @param int $displayErrors The display_errors flag value
735 *
736 * @deprecated since version 2.6, to be removed in 3.0. Use throwAt() instead.
737 */
738 public function setDisplayErrors($displayErrors)
739 {
740 @trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the throwAt() method instead.', E_USER_DEPRECATED);
741
742 if ($displayErrors) {
743 $this->throwAt($this->displayErrors, true);
744 } else {
745 $displayErrors = $this->displayErrors;
746 $this->throwAt(0, true);
747 $this->displayErrors = $displayErrors;
748 }
749 }
750
751 /**
752 * Sets a logger for the given channel.
753 *
754 * @param LoggerInterface $logger A logger interface
755 * @param string $channel The channel associated with the logger (deprecation, emergency or scream)
756 *
757 * @deprecated since version 2.6, to be removed in 3.0. Use setLoggers() or setDefaultLogger() instead.
758 */
759 public static function setLogger(LoggerInterface $logger, $channel = 'deprecation')
760 {
761 @trigger_error('The '.__METHOD__.' static method is deprecated since version 2.6 and will be removed in 3.0. Use the setLoggers() or setDefaultLogger() methods instead.', E_USER_DEPRECATED);
762
763 $handler = set_error_handler('var_dump');
764 $handler = is_array($handler) ? $handler[0] : null;
765 restore_error_handler();
766 if (!$handler instanceof self) {
767 return;
768 }
769 if ('deprecation' === $channel) {
770 $handler->setDefaultLogger($logger, E_DEPRECATED | E_USER_DEPRECATED, true);
771 $handler->screamAt(E_DEPRECATED | E_USER_DEPRECATED);
772 } elseif ('scream' === $channel) {
773 $handler->setDefaultLogger($logger, E_ALL | E_STRICT, false);
774 $handler->screamAt(E_ALL | E_STRICT);
775 } elseif ('emergency' === $channel) {
776 $handler->setDefaultLogger($logger, E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR, true);
777 $handler->screamAt(E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR);
778 }
779 }
780
781 /**
782 * @deprecated since version 2.6, to be removed in 3.0. Use handleError() instead.
783 */
784 public function handle($level, $message, $file = 'unknown', $line = 0, $context = array())
785 {
786 $this->handleError(E_USER_DEPRECATED, 'The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the handleError() method instead.', __FILE__, __LINE__, array());
787
788 return $this->handleError($level, $message, $file, $line, (array) $context);
789 }
790
791 /**
792 * Handles PHP fatal errors.
793 *
794 * @deprecated since version 2.6, to be removed in 3.0. Use handleFatalError() instead.
795 */
796 public function handleFatal()
797 {
798 @trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the handleFatalError() method instead.', E_USER_DEPRECATED);
799
800 static::handleFatalError();
801 }
802 }
803
804 /**
805 * Private class used to work around https://bugs.php.net/54275.
806 *
807 * @author Nicolas Grekas <p@tchwork.com>
808 *
809 * @internal
810 */
811 class ErrorHandlerCanary
812 {
813 private static $displayErrors = null;
814
815 public function __construct()
816 {
817 if (null === self::$displayErrors) {
818 self::$displayErrors = ini_set('display_errors', 1);
819 }
820 }
821
822 public function __destruct()
823 {
824 if (null !== self::$displayErrors) {
825 ini_set('display_errors', self::$displayErrors);
826 self::$displayErrors = null;
827 }
828 }
829 }
830