Commit 700499c2 authored by alexpott's avatar alexpott
Browse files

Issue #2463285 by stefan.r: Support PHP7 EngineExceptions in the error handler

parent d29700fb
......@@ -680,7 +680,7 @@ function _drupal_error_handler($error_level, $message, $filename, $line, $contex
* always fatal: the execution of the script will stop as soon as the exception
* handler exits.
*
* @param $exception
* @param \Exception|\BaseException $exception
* The exception object that was thrown.
*/
function _drupal_exception_handler($exception) {
......@@ -690,14 +690,32 @@ function _drupal_exception_handler($exception) {
// Log the message to the watchdog and return an error page to the user.
_drupal_log_error(Error::decodeException($exception), TRUE);
}
// PHP 7 introduces BaseExceptions.
catch (BaseException $exception2) {
_drupal_exception_handler_additional($exception, $exception2);
}
// In order to be compatibile with PHP 5 we also catch regular Exceptions.
catch (Exception $exception2) {
// Another uncaught exception was thrown while handling the first one.
// If we are displaying errors, then do so with no possibility of a further uncaught exception being thrown.
if (error_displayable()) {
print '<h1>Additional uncaught exception thrown while handling exception.</h1>';
print '<h2>Original</h2><p>' . Error::renderExceptionSafe($exception) . '</p>';
print '<h2>Additional</h2><p>' . Error::renderExceptionSafe($exception2) . '</p><hr />';
}
_drupal_exception_handler_additional($exception, $exception2);
}
}
/**
* Displays any additional errors caught while handling an exception.
*
* @param \Exception|\BaseException $exception
* The first exception object that was thrown.
* @param \Exception|\BaseException $exception
* The second exception object that was thrown.
*/
function _drupal_exception_handler_additional($exception, $exception2) {
// Another uncaught exception was thrown while handling the first one.
// If we are displaying errors, then do so with no possibility of a further
// uncaught exception being thrown.
if (error_displayable()) {
print '<h1>Additional uncaught exception thrown while handling exception.</h1>';
print '<h2>Original</h2><p>' . Error::renderExceptionSafe($exception) . '</p>';
print '<h2>Additional</h2><p>' . Error::renderExceptionSafe($exception2) . '</p><hr />';
}
}
......@@ -1081,18 +1099,35 @@ function _drupal_shutdown_function() {
call_user_func_array($callback['callback'], $callback['arguments']);
}
}
// PHP 7 introduces BaseExceptions.
catch (BaseException $exception) {
_drupal_shutdown_function_handle_exception($exception);
}
// In order to be compatibile with PHP 5 we also catch regular Exceptions.
catch (Exception $exception) {
// If using PHP-FPM then fastcgi_finish_request() will have been fired
// preventing further output to the browser.
if (!function_exists('fastcgi_finish_request')) {
// If we are displaying errors, then do so with no possibility of a
// further uncaught exception being thrown.
require_once __DIR__ . '/errors.inc';
if (error_displayable()) {
print '<h1>Uncaught exception thrown in shutdown function.</h1>';
print '<p>' . Error::renderExceptionSafe($exception) . '</p><hr />';
}
_drupal_shutdown_function_handle_exception($exception);
}
}
/**
* Displays and logs any errors that may happen during shutdown.
*
* @param \Exception|\BaseException $exception
* The exception object that was thrown.
*
* @see _drupal_shutdown_function()
*/
function _drupal_shutdown_function_handle_exception($exception) {
// If using PHP-FPM then fastcgi_finish_request() will have been fired
// preventing further output to the browser.
if (!function_exists('fastcgi_finish_request')) {
// If we are displaying errors, then do so with no possibility of a
// further uncaught exception being thrown.
require_once __DIR__ . '/errors.inc';
if (error_displayable()) {
print '<h1>Uncaught exception thrown in shutdown function.</h1>';
print '<p>' . Error::renderExceptionSafe($exception) . '</p><hr />';
}
error_log($exception);
}
error_log($exception);
}
......@@ -33,13 +33,13 @@ class Error {
/**
* Decodes an exception and retrieves the correct caller.
*
* @param \Exception $exception
* @param \Exception|\BaseException $exception
* The exception object that was thrown.
*
* @return array
* An error in the format expected by _drupal_log_error().
*/
public static function decodeException(\Exception $exception) {
public static function decodeException($exception) {
$message = $exception->getMessage();
$backtrace = $exception->getTrace();
......@@ -82,13 +82,13 @@ public static function decodeException(\Exception $exception) {
/**
* Renders an exception error message without further exceptions.
*
* @param \Exception $exception
* @param \Exception|\BaseException $exception
* The exception object that was thrown.
*
* @return string
* An error message.
*/
public static function renderExceptionSafe(\Exception $exception) {
public static function renderExceptionSafe($exception) {
$decode = static::decodeException($exception);
$backtrace = $decode['backtrace'];
unset($decode['backtrace']);
......
......@@ -51,6 +51,12 @@ function testErrorHandler() {
'%function' => 'Drupal\error_test\Controller\ErrorTestController->Drupal\error_test\Controller\{closure}()',
'!message' => 'Argument 1 passed to Drupal\error_test\Controller\ErrorTestController::Drupal\error_test\Controller\{closure}() must be of the type array, string given, called in ' . \Drupal::root() . '/core/modules/system/tests/modules/error_test/src/Controller/ErrorTestController.php on line 66 and defined',
);
if (version_compare(PHP_VERSION, '7.0.0-dev') >= 0) {
// In PHP 7, instead of a recoverable fatal error we get a TypeException.
$fatal_error['%type'] = 'TypeException';
// The error message also changes in PHP 7.
$fatal_error['!message'] = 'Argument 1 passed to Drupal\error_test\Controller\ErrorTestController::Drupal\error_test\Controller\{closure}() must be of the type array, string given, called in ' . \Drupal::root() . '/core/modules/system/tests/modules/error_test/src/Controller/ErrorTestController.php on line 66';
}
// Set error reporting to display verbose notices.
$this->config('system.logging')->set('error_level', ERROR_REPORTING_DISPLAY_VERBOSE)->save();
......@@ -68,10 +74,11 @@ function testErrorHandler() {
$this->assertErrorMessage($fatal_error);
$this->assertRaw('<pre class="backtrace">', 'Found pre element with backtrace class.');
// Remove the fatal error from the assertions, its wanted here. Ensure
// that we just remove the one recoverable fatal error.
// Remove the recoverable fatal error from the assertions, it's wanted here.
// Ensure that we just remove this one recoverable fatal error (in PHP 7 this
// is a TypeException).
foreach ($this->assertions as $key => $assertion) {
if ($assertion['message_group'] == 'Recoverable fatal error' && strpos($assertion['message'], 'Argument 1 passed to Drupal\error_test\Controller\ErrorTestController::Drupal\error_test\Controller\{closure}() must be of the type array, string given, called in') !== FALSE) {
if (in_array($assertion['message_group'], ['Recoverable fatal error', 'TypeException']) && strpos($assertion['message'], 'Argument 1 passed to Drupal\error_test\Controller\ErrorTestController::Drupal\error_test\Controller\{closure}() must be of the type array, string given, called in') !== FALSE) {
unset($this->assertions[$key]);
$this->deleteAssert($assertion['message_id']);
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment