Merge pull request #5673 from davidsteinsland/fix_exception_handler_nesting

Fixed nesting when ExceptionRenderer throws exception
This commit is contained in:
Mark Story 2015-01-28 22:00:57 -05:00
commit 151f6e90e0
2 changed files with 83 additions and 2 deletions

View file

@ -95,6 +95,14 @@ App::uses('Router', 'Routing');
*/ */
class ErrorHandler { class ErrorHandler {
/**
* Whether to give up rendering an exception, if the renderer itself is
* throwing exceptions.
*
* @var bool
*/
protected static $_bailExceptionRendering = false;
/** /**
* Set as the default exception handler by the CakePHP bootstrap process. * Set as the default exception handler by the CakePHP bootstrap process.
* *
@ -125,6 +133,8 @@ class ErrorHandler {
$e->getMessage(), $e->getMessage(),
$e->getTraceAsString() $e->getTraceAsString()
); );
self::$_bailExceptionRendering = true;
trigger_error($message, E_USER_ERROR); trigger_error($message, E_USER_ERROR);
} }
} }
@ -234,6 +244,8 @@ class ErrorHandler {
* @param string $file File on which error occurred * @param string $file File on which error occurred
* @param int $line Line that triggered the error * @param int $line Line that triggered the error
* @return bool * @return bool
* @throws FatalErrorException If the Exception renderer threw an exception during rendering, and debug > 0.
* @throws InternalErrorException If the Exception renderer threw an exception during rendering, and debug is 0.
*/ */
public static function handleFatalError($code, $description, $file, $line) { public static function handleFatalError($code, $description, $file, $line) {
$logMessage = 'Fatal Error (' . $code . '): ' . $description . ' in [' . $file . ', line ' . $line . ']'; $logMessage = 'Fatal Error (' . $code . '): ' . $description . ' in [' . $file . ', line ' . $line . ']';
@ -249,10 +261,18 @@ class ErrorHandler {
} }
if (Configure::read('debug')) { if (Configure::read('debug')) {
call_user_func($exceptionHandler, new FatalErrorException($description, 500, $file, $line)); $exception = new FatalErrorException($description, 500, $file, $line);
} else { } else {
call_user_func($exceptionHandler, new InternalErrorException()); $exception = new InternalErrorException();
} }
if (self::$_bailExceptionRendering) {
self::$_bailExceptionRendering = false;
throw $exception;
}
call_user_func($exceptionHandler, $exception);
return false; return false;
} }

View file

@ -20,6 +20,23 @@ App::uses('ErrorHandler', 'Error');
App::uses('Controller', 'Controller'); App::uses('Controller', 'Controller');
App::uses('Router', 'Routing'); App::uses('Router', 'Routing');
/**
* A faulty ExceptionRenderer to test nesting.
*/
class FaultyExceptionRenderer extends ExceptionRenderer {
/**
* Dummy rendering implementation.
*
* @return void
* @throws Exception
*/
public function render() {
throw new Exception('Error from renderer.');
}
}
/** /**
* ErrorHandlerTest class * ErrorHandlerTest class
* *
@ -320,4 +337,48 @@ class ErrorHandlerTest extends CakeTestCase {
$this->assertContains('[FatalErrorException] Something wrong', $log[1], 'message missing.'); $this->assertContains('[FatalErrorException] Something wrong', $log[1], 'message missing.');
} }
/**
* testExceptionRendererNestingDebug method
*
* @return void
*/
public function testExceptionRendererNestingDebug() {
Configure::write('debug', 2);
Configure::write('Exception.renderer', 'FaultyExceptionRenderer');
$result = false;
try {
ob_start();
ob_start();
ErrorHandler::handleFatalError(E_USER_ERROR, 'Initial error', __FILE__, __LINE__);
} catch (Exception $e) {
$result = $e instanceof FatalErrorException;
}
restore_error_handler();
$this->assertTrue($result);
}
/**
* testExceptionRendererNestingProduction method
*
* @return void
*/
public function testExceptionRendererNestingProduction() {
Configure::write('debug', 0);
Configure::write('Exception.renderer', 'FaultyExceptionRenderer');
$result = false;
try {
ob_start();
ob_start();
ErrorHandler::handleFatalError(E_USER_ERROR, 'Initial error', __FILE__, __LINE__);
} catch (Exception $e) {
$result = $e instanceof InternalErrorException;
}
restore_error_handler();
$this->assertTrue($result);
}
} }