mirror of
https://github.com/yiisoft/yii2.git
synced 2025-08-26 14:26:54 +08:00
More on error handling:
- Correct exit code for require errors in CLI mode. It was 0 even in case of error. - Solved handling fatals and displaying custom error template w/o output buffering. - If XDEBUG is available, error screen will display trace (implementation inspired by Kohana and Nette). - Added ErrorException to store/display type of the error and user-friendly messages. - Fatals are still logged in PHP log. - Turned off native display_errors since we're now catching all errors. - Added reserving memory (256kb) . Can be removed safely. In case of removal we're losing only memory exhausted error when allocating last very small chunk of memory. - Properly handled errors in __toString (exception can't be thrown in this case). - In the YII_DEBUG===false mode it's bad to display exception name even in page title. - In the YII_DEBUG===true mode it's still useful to get user-friendly message additionally to exception name.
This commit is contained in:
@ -96,6 +96,12 @@ class Application extends Module
|
|||||||
private $_ended = false;
|
private $_ended = false;
|
||||||
private $_language;
|
private $_language;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string Used to reserve memory for fatal error handler. This memory
|
||||||
|
* reserve can be removed if it's OK to write to PHP log only in this particular case.
|
||||||
|
*/
|
||||||
|
private $_memoryReserve;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
* @param string $id the ID of this application. The ID should uniquely identify the application from others.
|
* @param string $id the ID of this application. The ID should uniquely identify the application from others.
|
||||||
@ -110,6 +116,7 @@ class Application extends Module
|
|||||||
$this->setBasePath($basePath);
|
$this->setBasePath($basePath);
|
||||||
|
|
||||||
if (YII_ENABLE_ERROR_HANDLER) {
|
if (YII_ENABLE_ERROR_HANDLER) {
|
||||||
|
ini_set('display_errors', 0);
|
||||||
set_exception_handler(array($this, 'handleException'));
|
set_exception_handler(array($this, 'handleException'));
|
||||||
set_error_handler(array($this, 'handleError'), error_reporting());
|
set_error_handler(array($this, 'handleError'), error_reporting());
|
||||||
}
|
}
|
||||||
@ -126,7 +133,6 @@ class Application extends Module
|
|||||||
*/
|
*/
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
ob_start();
|
|
||||||
$this->preloadComponents();
|
$this->preloadComponents();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,27 +145,54 @@ class Application extends Module
|
|||||||
*/
|
*/
|
||||||
public function end($status = 0, $exit = true)
|
public function end($status = 0, $exit = true)
|
||||||
{
|
{
|
||||||
$lastError = error_get_last();
|
|
||||||
|
|
||||||
if(isset($lastError['type']) && in_array($lastError['type'], array(E_ERROR, E_PARSE))) {
|
|
||||||
ob_end_clean();
|
|
||||||
$exception = new \ErrorException($lastError['message'], 0, $lastError['type'], $lastError['file'], $lastError['line']);
|
|
||||||
$this->logException($exception);
|
|
||||||
|
|
||||||
if (($handler = $this->getErrorHandler()) !== null) {
|
|
||||||
$handler->handle($exception);
|
|
||||||
} else {
|
|
||||||
$this->renderException($exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
die(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->_ended) {
|
if (!$this->_ended) {
|
||||||
$this->_ended = true;
|
$this->_ended = true;
|
||||||
$this->afterRequest();
|
$this->afterRequest();
|
||||||
}
|
}
|
||||||
ob_end_flush();
|
|
||||||
|
if(YII_ENABLE_ERROR_HANDLER) {
|
||||||
|
$error = error_get_last();
|
||||||
|
|
||||||
|
if(isset($error['type']) && in_array($error['type'], ErrorException::getFatalCodes())) {
|
||||||
|
unset($this->_memoryReserve);
|
||||||
|
$exception = new ErrorException($error['message'], $error['type'], $error['type'], $error['file'], $error['line']);
|
||||||
|
|
||||||
|
if(function_exists('xdebug_get_function_stack')) {
|
||||||
|
$trace = array_slice(array_reverse(xdebug_get_function_stack()), 4, -1);
|
||||||
|
foreach($trace 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'] = '::';
|
||||||
|
}
|
||||||
|
|
||||||
|
// XDebug has a different key name
|
||||||
|
$frame['args'] = array();
|
||||||
|
if(isset($frame['params']) && !isset($frame['args'])) {
|
||||||
|
$frame['args'] = $frame['params'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$ref = new \ReflectionProperty('Exception', 'trace');
|
||||||
|
$ref->setAccessible(true);
|
||||||
|
$ref->setValue($exception, $trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->logException($exception);
|
||||||
|
|
||||||
|
if (($handler = $this->getErrorHandler()) !== null) {
|
||||||
|
$handler->handle($exception);
|
||||||
|
} else {
|
||||||
|
$this->renderException($exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
$status = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($exit) {
|
if ($exit) {
|
||||||
exit($status);
|
exit($status);
|
||||||
}
|
}
|
||||||
@ -173,6 +206,9 @@ class Application extends Module
|
|||||||
public function run()
|
public function run()
|
||||||
{
|
{
|
||||||
$this->beforeRequest();
|
$this->beforeRequest();
|
||||||
|
// Allocating twice more than required to display memory exhausted error
|
||||||
|
// in case of trying to allocate last 1 byte while all memory is taken.
|
||||||
|
$this->_memoryReserve = str_repeat('x', 1024*256);
|
||||||
register_shutdown_function(array($this,'end'),0,false);
|
register_shutdown_function(array($this,'end'),0,false);
|
||||||
$status = $this->processRequest();
|
$status = $this->processRequest();
|
||||||
$this->afterRequest();
|
$this->afterRequest();
|
||||||
@ -394,12 +430,24 @@ class Application extends Module
|
|||||||
* @param string $message the error message
|
* @param string $message the error message
|
||||||
* @param string $file the filename that the error was raised in
|
* @param string $file the filename that the error was raised in
|
||||||
* @param integer $line the line number the error was raised at
|
* @param integer $line the line number the error was raised at
|
||||||
* @throws \ErrorException the error exception
|
*
|
||||||
|
* @throws ErrorException
|
||||||
*/
|
*/
|
||||||
public function handleError($code, $message, $file, $line)
|
public function handleError($code, $message, $file, $line)
|
||||||
{
|
{
|
||||||
if (error_reporting() !== 0) {
|
if (error_reporting() !== 0) {
|
||||||
throw new \ErrorException($message, 0, $code, $file, $line);
|
$exception = new ErrorException($message, $code, $code, $file, $line);
|
||||||
|
|
||||||
|
// in case error appeared in __toString method we can't throw any exception
|
||||||
|
$trace = debug_backtrace(false);
|
||||||
|
array_shift($trace);
|
||||||
|
foreach($trace as $frame) {
|
||||||
|
if($frame['function'] == '__toString') {
|
||||||
|
$this->handleException($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw $exception;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
47
framework/base/ErrorException.php
Normal file
47
framework/base/ErrorException.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* ErrorException class file.
|
||||||
|
*
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright Copyright © 2008 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace yii\base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ErrorException represents a PHP error.
|
||||||
|
*
|
||||||
|
* @author Alexander Makarov <sam@rmcreative.ru>
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
class ErrorException extends \ErrorException
|
||||||
|
{
|
||||||
|
public static function getFatalCodes()
|
||||||
|
{
|
||||||
|
return array(E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string the user-friendly name of this exception
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
$names = array(
|
||||||
|
E_ERROR => \Yii::t('yii|Fatal Error'),
|
||||||
|
E_PARSE => \Yii::t('yii|Parse Error'),
|
||||||
|
E_CORE_ERROR => \Yii::t('yii|Core Error'),
|
||||||
|
E_COMPILE_ERROR => \Yii::t('yii|Compile Error'),
|
||||||
|
E_USER_ERROR => \Yii::t('yii|User Error'),
|
||||||
|
E_WARNING => \Yii::t('yii|Warning'),
|
||||||
|
E_CORE_WARNING => \Yii::t('yii|Core Warning'),
|
||||||
|
E_COMPILE_WARNING => \Yii::t('yii|Compile Warning'),
|
||||||
|
E_USER_WARNING => \Yii::t('yii|User Warning'),
|
||||||
|
E_STRICT => \Yii::t('yii|Strict'),
|
||||||
|
E_NOTICE => \Yii::t('yii|Notice'),
|
||||||
|
E_RECOVERABLE_ERROR => \Yii::t('yii|Recoverable Error'),
|
||||||
|
E_DEPRECATED => \Yii::t('yii|Deprecated'),
|
||||||
|
);
|
||||||
|
return isset($names[$this->getCode()]) ? $names[$this->getCode()] : \Yii::t('yii|Error');
|
||||||
|
}
|
||||||
|
}
|
@ -24,5 +24,4 @@ class Exception extends \Exception
|
|||||||
{
|
{
|
||||||
return \Yii::t('yii|Exception');
|
return \Yii::t('yii|Exception');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,12 +4,13 @@
|
|||||||
* @var \yii\base\ErrorHandler $owner
|
* @var \yii\base\ErrorHandler $owner
|
||||||
*/
|
*/
|
||||||
$owner = $this->owner;
|
$owner = $this->owner;
|
||||||
|
$title = $owner->htmlEncode($exception instanceof \yii\base\Exception || $exception instanceof \yii\base\ErrorException ? $exception->getName() : get_class($exception));
|
||||||
?>
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title><?php echo get_class($exception)?></title>
|
<title><?php echo $title?></title>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
@ -50,8 +51,8 @@ $owner = $this->owner;
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h1><?php echo $owner->htmlEncode($exception instanceof \yii\base\Exception ? $exception->getName() : get_class($exception)); ?></h1>
|
<h1><?php echo $title?></h1>
|
||||||
<h2><?php echo nl2br($owner->htmlEncode($exception->getMessage()))?> </h2>
|
<h2><?php echo nl2br($owner->htmlEncode($exception->getMessage()))?></h2>
|
||||||
<p>
|
<p>
|
||||||
The above error occurred while the Web server was processing your request.
|
The above error occurred while the Web server was processing your request.
|
||||||
</p>
|
</p>
|
||||||
|
@ -4,12 +4,13 @@
|
|||||||
* @var \yii\base\ErrorHandler $owner
|
* @var \yii\base\ErrorHandler $owner
|
||||||
*/
|
*/
|
||||||
$owner = $this->owner;
|
$owner = $this->owner;
|
||||||
|
$title = $owner->htmlEncode($exception instanceof \yii\base\Exception || $exception instanceof \yii\base\ErrorException ? $exception->getName().' ('.get_class($exception).')' : get_class($exception));
|
||||||
?>
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title><?php echo get_class($exception)?></title>
|
<title><?php echo $title?></title>
|
||||||
<style>
|
<style>
|
||||||
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent;margin:0;padding:0;}
|
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent;margin:0;padding:0;}
|
||||||
body{line-height:1;}
|
body{line-height:1;}
|
||||||
@ -160,7 +161,7 @@ $owner = $this->owner;
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1><?php echo get_class($exception)?></h1>
|
<h1><?php echo $title?></h1>
|
||||||
|
|
||||||
<p class="message">
|
<p class="message">
|
||||||
<?php echo nl2br($owner->htmlEncode($exception->getMessage()))?>
|
<?php echo nl2br($owner->htmlEncode($exception->getMessage()))?>
|
||||||
|
Reference in New Issue
Block a user