Browse Source

Fixes #8933: Yii is now able to properly handle HHVM fatal errors

tags/2.0.6
adinata 9 years ago committed by Alexander Makarov
parent
commit
8ced2ba96a
  1. 1
      framework/CHANGELOG.md
  2. 11
      framework/base/ErrorException.php
  3. 61
      framework/base/ErrorHandler.php

1
framework/CHANGELOG.md

@ -50,6 +50,7 @@ Yii Framework 2 Change Log
- Enh #8670: Added support for saving extra fields in session table for `yii\web\DbSession` (klimov-paul)
- Enh #8671: Extracted `yii\helpers\Html::escapeJsRegularExpression()` method from `yii\validators\RegularExpressionValidator` (silverfire, klimov-paul, samdark, qiangxue)
- Enh #8903: PostgreSQL `QueryBuilder::createIndex()` can now specify the index method to use (LAV45)
- Enh #8933: Yii is now able to properly handle HHVM fatal errors (dieend, samdark)
- Enh #9011: Allow `yii\widgets\MaskedInput` to produce an input tag of a custom type (TriAnMan)
- Enh #9038: Write warning to log in case `FileCache` fails to write into file (foccy)
- Enh #9072: `yii\web\ErrorAction` displays 404 error instead of blank page on direct access (klimov-paul)

11
framework/base/ErrorException.php

@ -17,6 +17,14 @@ use Yii;
*/
class ErrorException extends \ErrorException
{
/**
* Zend runtime won't call the error handler on fatals, HHVM will, with an error code of 16777217
* We will handle fatal error a bit different on HHVM.
* @see https://github.com/facebook/hhvm/blob/master/hphp/runtime/base/runtime-error.h#L62
*/
const E_HHVM_FATAL_ERROR = 16777217; // E_ERROR | (1 << 24)
/**
* Constructs the exception.
* @link http://php.net/manual/en/errorexception.construct.php
@ -65,7 +73,7 @@ class ErrorException extends \ErrorException
*/
public static function isFatalError($error)
{
return isset($error['type']) && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING]);
return isset($error['type']) && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, self::E_HHVM_FATAL_ERROR]);
}
/**
@ -89,6 +97,7 @@ class ErrorException extends \ErrorException
E_USER_NOTICE => 'PHP User Notice',
E_USER_WARNING => 'PHP User Warning',
E_WARNING => 'PHP Warning',
self::E_HHVM_FATAL_ERROR => 'HHVM Fatal Error',
];
return isset($names[$this->getCode()]) ? $names[$this->getCode()] : 'Error';

61
framework/base/ErrorHandler.php

@ -45,6 +45,10 @@ abstract class ErrorHandler extends Component
*/
private $_memoryReserve;
/**
* @var \Exception from HHVM error that stores backtrace
*/
private $_hhvmException;
/**
* Register this error handler
@ -53,7 +57,11 @@ abstract class ErrorHandler extends Component
{
ini_set('display_errors', false);
set_exception_handler([$this, 'handleException']);
set_error_handler([$this, 'handleError']);
if (defined('HHVM_VERSION')) {
set_error_handler([$this, 'handleHhvmError']);
} else {
set_error_handler([$this, 'handleError']);
}
if ($this->memoryReserveSize > 0) {
$this->_memoryReserve = str_repeat('x', $this->memoryReserveSize);
}
@ -100,6 +108,10 @@ abstract class ErrorHandler extends Component
}
$this->renderException($exception);
if (!YII_ENV_TEST) {
\Yii::getLogger()->flush(true);
if (defined('HHVM_VERSION')) {
flush();
}
exit(1);
}
} catch (\Exception $e) {
@ -119,11 +131,45 @@ abstract class ErrorHandler extends Component
}
$msg .= "\n\$_SERVER = " . VarDumper::export($_SERVER);
error_log($msg);
if (defined('HHVM_VERSION')) {
flush();
}
exit(1);
}
$this->exception = null;
}
/**
* Handles HHVM execution errors such as warnings and notices.
*
* This method is used as a HHVM error handler. It will store exception that will
* be used in fatal error handler
*
* @param integer $code the level of the error raised.
* @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.
* @param mixed $context
* @param mixed $backtrace trace of error
* @return boolean whether the normal error handler continues.
*
* @throws ErrorException
* @since 2.0.6
*/
public function handleHhvmError($code, $message, $file, $line, $context, $backtrace)
{
if ($this->handleError($code, $message, $file, $line)) {
return true;
}
if (E_ERROR & $code) {
$exception = new ErrorException($message, $code, $code, $file, $line);
$ref = new \ReflectionProperty('\Exception', 'trace');
$ref->setAccessible(true);
$ref->setValue($exception, $backtrace);
$this->_hhvmException = $exception;
}
return false;
}
/**
* Handles PHP execution errors such as warnings and notices.
@ -154,6 +200,9 @@ abstract class ErrorHandler extends Component
foreach ($trace as $frame) {
if ($frame['function'] == '__toString') {
$this->handleException($exception);
if (defined('HHVM_VERSION')) {
flush();
}
exit(1);
}
}
@ -179,7 +228,11 @@ abstract class ErrorHandler extends Component
$error = error_get_last();
if (ErrorException::isFatalError($error)) {
$exception = new ErrorException($error['message'], $error['type'], $error['type'], $error['file'], $error['line']);
if (!empty($this->_hhvmException)) {
$exception = $this->_hhvmException;
} else {
$exception = new ErrorException($error['message'], $error['type'], $error['type'], $error['file'], $error['line']);
}
$this->exception = $exception;
$this->logException($exception);
@ -191,7 +244,9 @@ abstract class ErrorHandler extends Component
// need to explicitly flush logs because exit() next will terminate the app immediately
Yii::getLogger()->flush(true);
if (defined('HHVM_VERSION')) {
flush();
}
exit(1);
}
}

Loading…
Cancel
Save