Skip to content
Permalink
Browse files

Ported getting trace for fatals in case of XDebug is installed

  • Loading branch information...
samdark committed Sep 18, 2019
1 parent 6be6192 commit d7df4fd8672494b9227443dc88fbded4a444a404
@@ -0,0 +1,93 @@
<?php
namespace Yiisoft\Yii\Web\ErrorHandler;
/**
* ErrorException represents a PHP error.
*/
class ErrorException extends \ErrorException implements FriendlyExceptionInterface
{
private const ERROR_NAMES = [
E_ERROR => 'PHP Fatal Error',
E_WARNING => 'PHP Warning',
E_PARSE => 'PHP Parse Error',
E_NOTICE => 'PHP Notice',
E_CORE_ERROR => 'PHP Core Error',
E_CORE_WARNING => 'PHP Core Warning',
E_COMPILE_ERROR => 'PHP Compile Error',
E_COMPILE_WARNING => 'PHP Compile Warning',
E_USER_ERROR => 'PHP User Error',
E_USER_WARNING => 'PHP User Warning',
E_USER_NOTICE => 'PHP User Notice',
E_STRICT => 'PHP Strict Warning',
E_RECOVERABLE_ERROR => 'PHP Recoverable Error',
E_DEPRECATED => 'PHP Deprecated Warning',
E_USER_DEPRECATED => 'PHP User Deprecated Warning',
];
public function __construct($message = '', $code = 0, $severity = 1, $filename = __FILE__, $lineno = __LINE__, \Exception $previous = null)
{
parent::__construct($message, $code, $severity, $filename, $lineno, $previous);
$this->addXDebugTraceToFatalIfAvailable();
}
/**
* Returns if error is one of fatal type.
*
* @param array $error error got from error_get_last()
* @return bool if error is one of fatal type
*/
public static function isFatalError(array $error): bool
{
return isset($error['type']) && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING], true);
}
/**
* @return string the user-friendly name of this exception
*/
public function getName(): string
{
return self::ERROR_NAMES[$this->getCode()] ?? 'Error';
}
/**
* Fatal errors normally do not provide any trace making it harder to debug. In case XDebug is installed, we
* can get a trace using xdebug_get_function_stack().
*/
private function addXDebugTraceToFatalIfAvailable(): void
{
if (function_exists('xdebug_get_function_stack')) {
// XDebug trace can't be modified and used directly with PHP 7
// @see https://github.com/yiisoft/yii2/pull/11723
$xDebugTrace = array_slice(array_reverse(xdebug_get_function_stack()), 1, -1);
$trace = [];
foreach ($xDebugTrace as $frame) {
if (!isset($frame['function'])) {
$frame['function'] = 'unknown';
}
// XDebug < 2.1.1: http://bugs.xdebug.org/view.php?id=695
if (!isset($frame['type']) || $frame['type'] === 'static') {
$frame['type'] = '::';
} elseif ($frame['type'] === 'dynamic') {
$frame['type'] = '->';
}
// XDebug has a different key name
if (isset($frame['params']) && !isset($frame['args'])) {
$frame['args'] = $frame['params'];
}
$trace[] = $frame;
}
$ref = new \ReflectionProperty('Exception', 'trace');
$ref->setAccessible(true);
$ref->setValue($this, $trace);
}
}
public function getSolution(): ?string
{
return null;
}
}
@@ -36,7 +36,7 @@ public function __construct(LoggerInterface $logger, ThrowableRendererInterface
* @param string $file the filename that the error was raised in.
* @param int $line the line number the error was raised at.
*
* @throws \ErrorException
* @throws ErrorException
*/
public function handleError(int $severity, string $message, string $file, int $line): void
{
@@ -55,7 +55,7 @@ public function handleError(int $severity, string $message, string $file, int $l
}
}
throw new \ErrorException($message, 0, $severity, $file, $line);
throw new ErrorException($message, $severity, $severity, $file, $line);
}
/**
@@ -131,18 +131,13 @@ public function handleFatalError(): void
{
unset($this->memoryReserve);
$error = error_get_last();
if ($error !== null && $this->isFatalError($error)) {
$exception = new \ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']);
if ($error !== null && ErrorException::isFatalError($error)) {
$exception = new ErrorException($error['message'], $error['type'], $error['type'], $error['file'], $error['line']);
$this->handleThrowable($exception);
exit(1);
}
}
private function isFatalError(array $error): bool
{
return isset($error['type']) && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING], true);
}
private function log(\Throwable $t/*, ServerRequestInterface $request*/): void
{
$renderer = new PlainTextRenderer();
@@ -0,0 +1,14 @@
<?php
namespace Yiisoft\Yii\Web\ErrorHandler;
/**
* FriendlyExceptionInterface could be implemented by exception in order to provide friendly name and a solution for
* fixing right it at the error screen
*/
interface FriendlyExceptionInterface
{
public function getName(): string;
public function getSolution(): ?string;
}

This file was deleted.

@@ -5,24 +5,6 @@
abstract class ThrowableRenderer implements ThrowableRendererInterface
{
private const SEVERITY_NAMES = [
E_COMPILE_ERROR => 'PHP Compile Error',
E_COMPILE_WARNING => 'PHP Compile Warning',
E_CORE_ERROR => 'PHP Core Error',
E_CORE_WARNING => 'PHP Core Warning',
E_DEPRECATED => 'PHP Deprecated Warning',
E_ERROR => 'PHP Fatal Error',
E_NOTICE => 'PHP Notice',
E_PARSE => 'PHP Parse Error',
E_RECOVERABLE_ERROR => 'PHP Recoverable Error',
E_STRICT => 'PHP Strict Warning',
E_USER_DEPRECATED => 'PHP User Deprecated Warning',
E_USER_ERROR => 'PHP User Error',
E_USER_NOTICE => 'PHP User Notice',
E_USER_WARNING => 'PHP User Warning',
E_WARNING => 'PHP Warning',
];
/**
* @var ServerRequestInterface
*/
@@ -32,8 +14,8 @@ protected function getThrowableName(\Throwable $t): string
{
$name = get_class($t);
if ($t instanceof \ErrorException && isset(self::SEVERITY_NAMES[$t->getSeverity()])) {
$name .= ' (' . self::SEVERITY_NAMES[$t->getSeverity()] . ')';
if ($t instanceof FriendlyExceptionInterface) {
$name = $t->getName() . ' (' . $name . ')';
}
return $name;
@@ -2,7 +2,7 @@
/* @var $throwable \Throwable */
/* @var $this \Yiisoft\Yii\Web\ErrorHandler\HtmlRenderer */
use Yiisoft\Yii\Web\ErrorHandler\SolutionInterface;
use Yiisoft\Yii\Web\ErrorHandler\FriendlyExceptionInterface;
?>
<!doctype html>
@@ -402,10 +402,15 @@
<?php endif; ?>
</div>
<h1>
<span><?= $this->addTypeLinks(get_class($throwable), $this->getThrowableName($throwable)) ?></span>
<?php if ($throwable instanceof FriendlyExceptionInterface): ?>
<span><?= $this->htmlEncode($throwable->getName())?></span>
<?= $this->addTypeLinks(get_class($throwable)) ?>
<?php else: ?>
<span><?= $this->addTypeLinks(get_class($throwable)) ?></span>
<?php endif ?>
</h1>
<h2><?= nl2br($this->htmlEncode($throwable->getMessage())) ?></h2>
<?php if ($throwable instanceof SolutionInterface): ?>
<?php if ($throwable instanceof FriendlyExceptionInterface && $throwable->getSolution() !== null): ?>
<div class="solution"><?= nl2br($this->htmlEncode($throwable->getSolution())) ?></div>
<?php endif ?>

0 comments on commit d7df4fd

Please sign in to comment.
You can’t perform that action at this time.