diff --git a/framework/base/Application.php b/framework/base/Application.php index 55dc0cc..30810d6 100644 --- a/framework/base/Application.php +++ b/framework/base/Application.php @@ -97,6 +97,12 @@ class Application extends Module 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. * @param string $id the ID of this application. The ID should uniquely identify the application from others. * @param string $basePath the base path of this application. This should point to @@ -110,6 +116,7 @@ class Application extends Module $this->setBasePath($basePath); if (YII_ENABLE_ERROR_HANDLER) { + ini_set('display_errors', 0); set_exception_handler(array($this, 'handleException')); set_error_handler(array($this, 'handleError'), error_reporting()); } @@ -142,12 +149,64 @@ class Application extends Module $this->_ended = true; $this->afterRequest(); } + + $this->handleFatalError(); + if ($exit) { exit($status); } } /** + * Handles fatal PHP errors + */ + public function handleFatalError() + { + if(YII_ENABLE_ERROR_HANDLER) { + $error = error_get_last(); + + if(ErrorException::isFatalErorr($error)) { + 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); + } + + exit(1); + } + } + } + + /** * Runs the application. * This is the main entrance of an application. * @return integer the exit status (0 means normal, non-zero values mean abnormal) @@ -155,6 +214,10 @@ class Application extends Module public function run() { $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); $status = $this->processRequest(); $this->afterRequest(); return $status; @@ -375,12 +438,24 @@ class Application extends Module * @param string $message the error message * @param string $file the filename that the error was raised in * @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) { 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; } } diff --git a/framework/base/ErrorException.php b/framework/base/ErrorException.php new file mode 100644 index 0000000..a1e0252 --- /dev/null +++ b/framework/base/ErrorException.php @@ -0,0 +1,83 @@ + + * @since 2.0 + */ +class ErrorException extends Exception +{ + protected $severity; + + /** + * Constructs the exception + * @link http://php.net/manual/en/errorexception.construct.php + * @param $message [optional] + * @param $code [optional] + * @param $severity [optional] + * @param $filename [optional] + * @param $lineno [optional] + * @param $previous [optional] + */ + public function __construct($message = '', $code = 0, $severity = 1, $filename = __FILE__, $lineno = __LINE__, \Exception $previous = null) + { + parent::__construct($message, $code, $previous); + $this->severity = $severity; + $this->file = $filename; + $this->line = $lineno; + } + + /** + * Gets the exception severity + * @link http://php.net/manual/en/errorexception.getseverity.php + * @return int the severity level of the exception. + */ + final public function getSeverity() + { + return $this->severity; + } + + /** + * 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 isFatalErorr($error) + { + return isset($error['type']) && in_array($error['type'], 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'); + } +} diff --git a/framework/base/Exception.php b/framework/base/Exception.php index 0088a55..7dcd7c5 100644 --- a/framework/base/Exception.php +++ b/framework/base/Exception.php @@ -24,5 +24,4 @@ class Exception extends \Exception { return \Yii::t('yii|Exception'); } -} - +} \ No newline at end of file diff --git a/framework/views/error.php b/framework/views/error.php index c1700f2..28c50f1 100644 --- a/framework/views/error.php +++ b/framework/views/error.php @@ -4,12 +4,13 @@ * @var \yii\base\ErrorHandler $owner */ $owner = $this->owner; +$title = $owner->htmlEncode($exception instanceof \yii\base\Exception || $exception instanceof \yii\base\ErrorException ? $exception->getName() : get_class($exception)); ?> - <?php echo get_class($exception)?> + <?php echo $title?>