Commit bb2d2799 authored by alexpott's avatar alexpott

Issue #2501319 by joelpittet, cwells, stefan.r, YesCT, nlisgo, Cottser,...

Issue #2501319 by joelpittet, cwells, stefan.r, YesCT, nlisgo, Cottser, cmanalansan, dawehner, xjm, alexpott, josephdpurcell: Remove SafeMarkup::set() in Error::renderExceptionSafe() and _drupal_log_error() and improve backtrace formatting consistency in _drupal_log_error(), Error::renderExceptionSafe(), and DefaultExceptionSubscriber::onHtml()
parent b728936c
...@@ -405,10 +405,7 @@ function watchdog_exception($type, Exception $exception, $message = NULL, $varia ...@@ -405,10 +405,7 @@ function watchdog_exception($type, Exception $exception, $message = NULL, $varia
// Use a default value if $message is not set. // Use a default value if $message is not set.
if (empty($message)) { if (empty($message)) {
// The exception message is run through $message = '%type: @message in %function (line %line of %file).';
// \Drupal\Component\Utility\SafeMarkup::checkPlain() by
// \Drupal\Core\Utility\Error:decodeException().
$message = '%type: !message in %function (line %line of %file).';
} }
if ($link) { if ($link) {
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
use Drupal\Component\Utility\SafeMarkup; use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Xss; use Drupal\Component\Utility\Xss;
use Drupal\Core\Logger\RfcLogLevel; use Drupal\Core\Logger\RfcLogLevel;
use Drupal\Core\Render\SafeString;
use Drupal\Core\Utility\Error; use Drupal\Core\Utility\Error;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
...@@ -68,7 +69,7 @@ function _drupal_error_handler_real($error_level, $message, $filename, $line, $c ...@@ -68,7 +69,7 @@ function _drupal_error_handler_real($error_level, $message, $filename, $line, $c
'%type' => isset($types[$error_level]) ? $severity_msg : 'Unknown error', '%type' => isset($types[$error_level]) ? $severity_msg : 'Unknown error',
// The standard PHP error handler considers that the error messages // The standard PHP error handler considers that the error messages
// are HTML. We mimick this behavior here. // are HTML. We mimick this behavior here.
'!message' => Xss::filterAdmin($message), '@message' => SafeString::create(Xss::filterAdmin($message)),
'%function' => $caller['function'], '%function' => $caller['function'],
'%file' => $caller['file'], '%file' => $caller['file'],
'%line' => $caller['line'], '%line' => $caller['line'],
...@@ -109,9 +110,9 @@ function error_displayable($error = NULL) { ...@@ -109,9 +110,9 @@ function error_displayable($error = NULL) {
* Logs a PHP error or exception and displays an error page in fatal cases. * Logs a PHP error or exception and displays an error page in fatal cases.
* *
* @param $error * @param $error
* An array with the following keys: %type, !message, %function, %file, * An array with the following keys: %type, @message, %function, %file,
* %line, severity_level, and backtrace. All the parameters are plain-text, * %line, severity_level, and backtrace. All the parameters are plain-text,
* with the exception of !message, which needs to be a safe HTML string, and * with the exception of @message, which needs to be an HTML string, and
* backtrace, which is a standard PHP backtrace. * backtrace, which is a standard PHP backtrace.
* @param $fatal * @param $fatal
* TRUE if the error is fatal. * TRUE if the error is fatal.
...@@ -130,7 +131,7 @@ function _drupal_log_error($error, $fatal = FALSE) { ...@@ -130,7 +131,7 @@ function _drupal_log_error($error, $fatal = FALSE) {
// as it uniquely identifies each PHP error. // as it uniquely identifies each PHP error.
static $number = 0; static $number = 0;
$assertion = array( $assertion = array(
$error['!message'], $error['@message'],
$error['%type'], $error['%type'],
array( array(
'function' => $error['%function'], 'function' => $error['%function'],
...@@ -154,12 +155,12 @@ function _drupal_log_error($error, $fatal = FALSE) { ...@@ -154,12 +155,12 @@ function _drupal_log_error($error, $fatal = FALSE) {
// installer. // installer.
if (\Drupal::hasService('logger.factory')) { if (\Drupal::hasService('logger.factory')) {
try { try {
\Drupal::logger('php')->log($error['severity_level'], '%type: !message in %function (line %line of %file).', $error); \Drupal::logger('php')->log($error['severity_level'], '%type: @message in %function (line %line of %file).', $error);
} }
catch (\Exception $e) { catch (\Exception $e) {
// We can't log, for example because the database connection is not // We can't log, for example because the database connection is not
// available. At least try to log to PHP error log. // available. At least try to log to PHP error log.
error_log(strtr('Failed to log error: %type: !message in %function (line %line of %file).', $error)); error_log(strtr('Failed to log error: %type: @message in %function (line %line of %file).', $error));
} }
} }
...@@ -167,7 +168,7 @@ function _drupal_log_error($error, $fatal = FALSE) { ...@@ -167,7 +168,7 @@ function _drupal_log_error($error, $fatal = FALSE) {
if ($fatal) { if ($fatal) {
// When called from CLI, simply output a plain text message. // When called from CLI, simply output a plain text message.
// Should not translate the string to avoid errors producing more errors. // Should not translate the string to avoid errors producing more errors.
$response->setContent(html_entity_decode(strip_tags(format_string('%type: !message in %function (line %line of %file).', $error))). "\n"); $response->setContent(html_entity_decode(strip_tags(SafeMarkup::format('%type: @message in %function (line %line of %file).', $error))). "\n");
$response->send(); $response->send();
exit; exit;
} }
...@@ -178,7 +179,7 @@ function _drupal_log_error($error, $fatal = FALSE) { ...@@ -178,7 +179,7 @@ function _drupal_log_error($error, $fatal = FALSE) {
if (error_displayable($error)) { if (error_displayable($error)) {
// When called from JavaScript, simply output the error message. // When called from JavaScript, simply output the error message.
// Should not translate the string to avoid errors producing more errors. // Should not translate the string to avoid errors producing more errors.
$response->setContent(format_string('%type: !message in %function (line %line of %file).', $error)); $response->setContent(SafeMarkup::format('%type: @message in %function (line %line of %file).', $error));
$response->send(); $response->send();
} }
exit; exit;
...@@ -208,20 +209,29 @@ function _drupal_log_error($error, $fatal = FALSE) { ...@@ -208,20 +209,29 @@ function _drupal_log_error($error, $fatal = FALSE) {
$error['%file'] = substr($error['%file'], $root_length + 1); $error['%file'] = substr($error['%file'], $root_length + 1);
} }
} }
// Should not translate the string to avoid errors producing more errors.
$message = format_string('%type: !message in %function (line %line of %file).', $error);
// Check if verbose error reporting is on. // Check if verbose error reporting is on.
$error_level = _drupal_get_error_level(); $error_level = _drupal_get_error_level();
if ($error_level == ERROR_REPORTING_DISPLAY_VERBOSE) { if ($error_level != ERROR_REPORTING_DISPLAY_VERBOSE) {
// Without verbose logging, use a simple message.
// We call SafeMarkup::format() directly here, rather than use t() since
// we are in the middle of error handling, and we don't want t() to
// cause further errors.
$message = SafeMarkup::format('%type: @message in %function (line %line of %file).', $error);
}
else {
// With verbose logging, we will also include a backtrace.
// First trace is the error itself, already contained in the message. // First trace is the error itself, already contained in the message.
// While the second trace is the error source and also contained in the // While the second trace is the error source and also contained in the
// message, the message doesn't contain argument values, so we output it // message, the message doesn't contain argument values, so we output it
// once more in the backtrace. // once more in the backtrace.
array_shift($backtrace); array_shift($backtrace);
// Generate a backtrace containing only scalar argument values. // Generate a backtrace containing only scalar argument values.
$message .= '<pre class="backtrace">' . Error::formatBacktrace($backtrace) . '</pre>'; $error['@backtrace'] = Error::formatBacktrace($backtrace);
$message = SafeMarkup::format('%type: @message in %function (line %line of %file). <pre class="backtrace">@backtrace</pre>', $error);
} }
} }
...@@ -252,7 +262,7 @@ function _drupal_log_error($error, $fatal = FALSE) { ...@@ -252,7 +262,7 @@ function _drupal_log_error($error, $fatal = FALSE) {
if ($message) { if ($message) {
if (\Drupal::hasService('session')) { if (\Drupal::hasService('session')) {
// Message display is dependent on sessions being available. // Message display is dependent on sessions being available.
drupal_set_message(SafeMarkup::set($message), $class, TRUE); drupal_set_message($message, $class, TRUE);
} }
else { else {
print $message; print $message;
......
...@@ -188,10 +188,7 @@ function update_do_one($module, $number, $dependency_map, &$context) { ...@@ -188,10 +188,7 @@ function update_do_one($module, $number, $dependency_map, &$context) {
$variables = Error::decodeException($e); $variables = Error::decodeException($e);
unset($variables['backtrace']); unset($variables['backtrace']);
// The exception message is run through $ret['#abort'] = array('success' => FALSE, 'query' => t('%type: @message in %function (line %line of %file).', $variables));
// \Drupal\Component\Utility\SafeMarkup::checkPlain() by
// \Drupal\Core\Utility\Error::decodeException().
$ret['#abort'] = array('success' => FALSE, 'query' => t('%type: !message in %function (line %line of %file).', $variables));
} }
} }
...@@ -235,10 +232,7 @@ function update_entity_definitions(&$context) { ...@@ -235,10 +232,7 @@ function update_entity_definitions(&$context) {
watchdog_exception('update', $e); watchdog_exception('update', $e);
$variables = Error::decodeException($e); $variables = Error::decodeException($e);
unset($variables['backtrace']); unset($variables['backtrace']);
// The exception message is run through $ret['#abort'] = array('success' => FALSE, 'query' => t('%type: @message in %function (line %line of %file).', $variables));
// \Drupal\Component\Utility\SafeMarkup::checkPlain() by
// \Drupal\Core\Utility\Error::decodeException().
$ret['#abort'] = array('success' => FALSE, 'query' => t('%type: !message in %function (line %line of %file).', $variables));
$context['results']['core']['update_entity_definitions'] = $ret; $context['results']['core']['update_entity_definitions'] = $ret;
$context['results']['#abort'][] = 'update_entity_definitions'; $context['results']['#abort'][] = 'update_entity_definitions';
} }
......
...@@ -161,7 +161,7 @@ protected function makeSubrequest(GetResponseForExceptionEvent $event, $url, $st ...@@ -161,7 +161,7 @@ protected function makeSubrequest(GetResponseForExceptionEvent $event, $url, $st
// just log it. The DefaultExceptionSubscriber will catch the original // just log it. The DefaultExceptionSubscriber will catch the original
// exception and handle it normally. // exception and handle it normally.
$error = Error::decodeException($e); $error = Error::decodeException($e);
$this->logger->log($error['severity_level'], '%type: !message in %function (line %line of %file).', $error); $this->logger->log($error['severity_level'], '%type: @message in %function (line %line of %file).', $error);
} }
} }
} }
......
...@@ -91,12 +91,20 @@ protected function onHtml(GetResponseForExceptionEvent $event) { ...@@ -91,12 +91,20 @@ protected function onHtml(GetResponseForExceptionEvent $event) {
if (substr($error['%file'], 0, $root_length) == DRUPAL_ROOT) { if (substr($error['%file'], 0, $root_length) == DRUPAL_ROOT) {
$error['%file'] = substr($error['%file'], $root_length + 1); $error['%file'] = substr($error['%file'], $root_length + 1);
} }
// Do not translate the string to avoid errors producing more errors.
unset($error['backtrace']); unset($error['backtrace']);
$message = SafeMarkup::format('%type: !message in %function (line %line of %file).', $error);
// Check if verbose error reporting is on. if ($this->getErrorLevel() != ERROR_REPORTING_DISPLAY_VERBOSE) {
if ($this->getErrorLevel() == ERROR_REPORTING_DISPLAY_VERBOSE) { // Without verbose logging, use a simple message.
// We call SafeMarkup::format directly here, rather than use t() since
// we are in the middle of error handling, and we don't want t() to
// cause further errors.
$message = SafeMarkup::format('%type: @message in %function (line %line of %file).', $error);
}
else {
// With verbose logging, we will also include a backtrace.
$backtrace_exception = $exception; $backtrace_exception = $exception;
while ($backtrace_exception->getPrevious()) { while ($backtrace_exception->getPrevious()) {
$backtrace_exception = $backtrace_exception->getPrevious(); $backtrace_exception = $backtrace_exception->getPrevious();
...@@ -108,9 +116,9 @@ protected function onHtml(GetResponseForExceptionEvent $event) { ...@@ -108,9 +116,9 @@ protected function onHtml(GetResponseForExceptionEvent $event) {
// once more in the backtrace. // once more in the backtrace.
array_shift($backtrace); array_shift($backtrace);
// Generate a backtrace containing only scalar argument values. Make // Generate a backtrace containing only scalar argument values.
// sure the backtrace is escaped as it can contain user submitted data. $error['@backtrace'] = Error::formatBacktrace($backtrace);
$message .= '<pre class="backtrace">' . SafeMarkup::escape(Error::formatBacktrace($backtrace)) . '</pre>'; $message = SafeMarkup::format('%type: @message in %function (line %line of %file). <pre class="backtrace">@backtrace</pre>', $error);
} }
} }
......
...@@ -68,7 +68,7 @@ public function on404(GetResponseForExceptionEvent $event) { ...@@ -68,7 +68,7 @@ public function on404(GetResponseForExceptionEvent $event) {
public function onError(GetResponseForExceptionEvent $event) { public function onError(GetResponseForExceptionEvent $event) {
$exception = $event->getException(); $exception = $event->getException();
$error = Error::decodeException($exception); $error = Error::decodeException($exception);
$this->logger->get('php')->log($error['severity_level'], '%type: !message in %function (line %line of %file).', $error); $this->logger->get('php')->log($error['severity_level'], '%type: @message in %function (line %line of %file).', $error);
$is_critical = !$exception instanceof HttpExceptionInterface || $exception->getStatusCode() >= 500; $is_critical = !$exception instanceof HttpExceptionInterface || $exception->getStatusCode() >= 500;
if ($is_critical) { if ($is_critical) {
......
...@@ -51,7 +51,7 @@ public function on500(GetResponseForExceptionEvent $event) { ...@@ -51,7 +51,7 @@ public function on500(GetResponseForExceptionEvent $event) {
// as it uniquely identifies each PHP error. // as it uniquely identifies each PHP error.
static $number = 0; static $number = 0;
$assertion = array( $assertion = array(
$error['!message'], $error['@message'],
$error['%type'], $error['%type'],
array( array(
'function' => $error['%function'], 'function' => $error['%function'],
......
...@@ -70,7 +70,7 @@ public static function decodeException($exception) { ...@@ -70,7 +70,7 @@ public static function decodeException($exception) {
'%type' => get_class($exception), '%type' => get_class($exception),
// The standard PHP exception handler considers that the exception message // The standard PHP exception handler considers that the exception message
// is plain-text. We mimic this behavior here. // is plain-text. We mimic this behavior here.
'!message' => SafeMarkup::checkPlain($message), '@message' => $message,
'%function' => $caller['function'], '%function' => $caller['function'],
'%file' => $caller['file'], '%file' => $caller['file'],
'%line' => $caller['line'], '%line' => $caller['line'],
...@@ -95,14 +95,13 @@ public static function renderExceptionSafe($exception) { ...@@ -95,14 +95,13 @@ public static function renderExceptionSafe($exception) {
// Remove 'main()'. // Remove 'main()'.
array_shift($backtrace); array_shift($backtrace);
$output = SafeMarkup::format('%type: !message in %function (line %line of %file).', $decode);
// Even though it is possible that this method is called on a public-facing // Even though it is possible that this method is called on a public-facing
// site, it is only called when the exception handler itself threw an // site, it is only called when the exception handler itself threw an
// exception, which normally means that a code change caused the system to // exception, which normally means that a code change caused the system to
// no longer function correctly (as opposed to a user-triggered error), so // no longer function correctly (as opposed to a user-triggered error), so
// we assume that it is safe to include a verbose backtrace. // we assume that it is safe to include a verbose backtrace.
$output .= '<pre>' . static::formatBacktrace($backtrace) . '</pre>'; $decode['@backtrace'] = Error::formatBacktrace($backtrace);
return SafeMarkup::set($output); return SafeMarkup::format('%type: @message in %function (line %line of %file). <pre class="backtrace">@backtrace</pre>', $decode);
} }
/** /**
......
...@@ -405,7 +405,7 @@ public function saveQueuedMessages() { ...@@ -405,7 +405,7 @@ public function saveQueuedMessages() {
*/ */
protected function handleException(\Exception $exception, $save = TRUE) { protected function handleException(\Exception $exception, $save = TRUE) {
$result = Error::decodeException($exception); $result = Error::decodeException($exception);
$message = $result['!message'] . ' (' . $result['%file'] . ':' . $result['%line'] . ')'; $message = $result['@message'] . ' (' . $result['%file'] . ':' . $result['%line'] . ')';
if ($save) { if ($save) {
$this->saveMessage($message); $this->saveMessage($message);
} }
......
...@@ -203,7 +203,7 @@ protected static function getWatchdogIdsForTestExceptionRollback() { ...@@ -203,7 +203,7 @@ protected static function getWatchdogIdsForTestExceptionRollback() {
$query = db_query("SELECT wid, variables FROM {watchdog}"); $query = db_query("SELECT wid, variables FROM {watchdog}");
foreach ($query as $row) { foreach ($query as $row) {
$variables = (array) unserialize($row->variables); $variables = (array) unserialize($row->variables);
if (isset($variables['!message']) && $variables['!message'] === 'Test exception for rollback.') { if (isset($variables['@message']) && $variables['@message'] === 'Test exception for rollback.') {
$matches[] = $row->wid; $matches[] = $row->wid;
} }
} }
......
...@@ -1384,12 +1384,10 @@ protected function exceptionHandler($exception) { ...@@ -1384,12 +1384,10 @@ protected function exceptionHandler($exception) {
'line' => $exception->getLine(), 'line' => $exception->getLine(),
'file' => $exception->getFile(), 'file' => $exception->getFile(),
)); ));
// \Drupal\Core\Utility\Error::decodeException() runs the exception
// message through \Drupal\Component\Utility\SafeMarkup::checkPlain().
$decoded_exception = Error::decodeException($exception); $decoded_exception = Error::decodeException($exception);
unset($decoded_exception['backtrace']); unset($decoded_exception['backtrace']);
$message = SafeMarkup::format('%type: !message in %function (line %line of %file). <pre class="backtrace">!backtrace</pre>', $decoded_exception + array( $message = SafeMarkup::format('%type: @message in %function (line %line of %file). <pre class="backtrace">@backtrace</pre>', $decoded_exception + array(
'!backtrace' => Error::formatBacktrace($verbose_backtrace), '@backtrace' => Error::formatBacktrace($verbose_backtrace),
)); ));
$this->error($message, 'Uncaught exception', Error::getLastCaller($backtrace)); $this->error($message, 'Uncaught exception', Error::getLastCaller($backtrace));
} }
......
...@@ -44,7 +44,7 @@ function testErrorCollect() { ...@@ -44,7 +44,7 @@ function testErrorCollect() {
if (count($this->collectedErrors) == 3) { if (count($this->collectedErrors) == 3) {
$this->assertError($this->collectedErrors[0], 'Notice', 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', 'ErrorTestController.php', 'Undefined variable: bananas'); $this->assertError($this->collectedErrors[0], 'Notice', 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', 'ErrorTestController.php', 'Undefined variable: bananas');
$this->assertError($this->collectedErrors[1], 'Warning', 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', 'ErrorTestController.php', 'Division by zero'); $this->assertError($this->collectedErrors[1], 'Warning', 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', 'ErrorTestController.php', 'Division by zero');
$this->assertError($this->collectedErrors[2], 'User warning', 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', 'ErrorTestController.php', 'Drupal is awesome'); $this->assertError($this->collectedErrors[2], 'User warning', 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', 'ErrorTestController.php', 'Drupal &amp; awesome');
} }
else { else {
// Give back the errors to the log report. // Give back the errors to the log report.
......
...@@ -30,32 +30,32 @@ function testErrorHandler() { ...@@ -30,32 +30,32 @@ function testErrorHandler() {
$config = $this->config('system.logging'); $config = $this->config('system.logging');
$error_notice = array( $error_notice = array(
'%type' => 'Notice', '%type' => 'Notice',
'!message' => 'Undefined variable: bananas', '@message' => 'Undefined variable: bananas',
'%function' => 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', '%function' => 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()',
'%file' => drupal_get_path('module', 'error_test') . '/error_test.module', '%file' => drupal_get_path('module', 'error_test') . '/error_test.module',
); );
$error_warning = array( $error_warning = array(
'%type' => 'Warning', '%type' => 'Warning',
'!message' => 'Division by zero', '@message' => 'Division by zero',
'%function' => 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', '%function' => 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()',
'%file' => drupal_get_path('module', 'error_test') . '/error_test.module', '%file' => drupal_get_path('module', 'error_test') . '/error_test.module',
); );
$error_user_notice = array( $error_user_notice = array(
'%type' => 'User warning', '%type' => 'User warning',
'!message' => 'Drupal is awesome', '@message' => 'Drupal & awesome',
'%function' => 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', '%function' => 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()',
'%file' => drupal_get_path('module', 'error_test') . '/error_test.module', '%file' => drupal_get_path('module', 'error_test') . '/error_test.module',
); );
$fatal_error = array( $fatal_error = array(
'%type' => 'Recoverable fatal error', '%type' => 'Recoverable fatal error',
'%function' => 'Drupal\error_test\Controller\ErrorTestController->Drupal\error_test\Controller\{closure}()', '%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', '@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) { if (version_compare(PHP_VERSION, '7.0.0-dev') >= 0) {
// In PHP 7, instead of a recoverable fatal error we get a TypeError. // In PHP 7, instead of a recoverable fatal error we get a TypeError.
$fatal_error['%type'] = 'TypeError'; $fatal_error['%type'] = 'TypeError';
// The error message also changes in PHP 7. // 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'; $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. // Set error reporting to display verbose notices.
...@@ -66,6 +66,9 @@ function testErrorHandler() { ...@@ -66,6 +66,9 @@ function testErrorHandler() {
$this->assertErrorMessage($error_warning); $this->assertErrorMessage($error_warning);
$this->assertErrorMessage($error_user_notice); $this->assertErrorMessage($error_user_notice);
$this->assertRaw('<pre class="backtrace">', 'Found pre element with backtrace class.'); $this->assertRaw('<pre class="backtrace">', 'Found pre element with backtrace class.');
// Ensure we are escaping but not double escaping.
$this->assertRaw('&amp;');
$this->assertNoRaw('&amp;amp;');
// Set error reporting to display verbose notices. // Set error reporting to display verbose notices.
$this->config('system.logging')->set('error_level', ERROR_REPORTING_DISPLAY_VERBOSE)->save(); $this->config('system.logging')->set('error_level', ERROR_REPORTING_DISPLAY_VERBOSE)->save();
...@@ -73,6 +76,9 @@ function testErrorHandler() { ...@@ -73,6 +76,9 @@ function testErrorHandler() {
$this->assertResponse(500, 'Received expected HTTP status code.'); $this->assertResponse(500, 'Received expected HTTP status code.');
$this->assertErrorMessage($fatal_error); $this->assertErrorMessage($fatal_error);
$this->assertRaw('<pre class="backtrace">', 'Found pre element with backtrace class.'); $this->assertRaw('<pre class="backtrace">', 'Found pre element with backtrace class.');
// Ensure we are escaping but not double escaping.
$this->assertRaw('&#039;');
$this->assertNoRaw('&amp;#039;');
// Remove the recoverable fatal error from the assertions, it's wanted here. // 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 // Ensure that we just remove this one recoverable fatal error (in PHP 7 this
...@@ -124,21 +130,21 @@ function testExceptionHandler() { ...@@ -124,21 +130,21 @@ function testExceptionHandler() {
$error_exception = array( $error_exception = array(
'%type' => 'Exception', '%type' => 'Exception',
'!message' => 'Drupal is awesome', '@message' => 'Drupal & awesome',
'%function' => 'Drupal\error_test\Controller\ErrorTestController->triggerException()', '%function' => 'Drupal\error_test\Controller\ErrorTestController->triggerException()',
'%line' => 56, '%line' => 56,
'%file' => drupal_get_path('module', 'error_test') . '/error_test.module', '%file' => drupal_get_path('module', 'error_test') . '/error_test.module',
); );
$error_pdo_exception = array( $error_pdo_exception = array(
'%type' => 'DatabaseExceptionWrapper', '%type' => 'DatabaseExceptionWrapper',
'!message' => 'SELECT * FROM bananas_are_awesome', '@message' => 'SELECT * FROM bananas_are_awesome',
'%function' => 'Drupal\error_test\Controller\ErrorTestController->triggerPDOException()', '%function' => 'Drupal\error_test\Controller\ErrorTestController->triggerPDOException()',
'%line' => 64, '%line' => 64,
'%file' => drupal_get_path('module', 'error_test') . '/error_test.module', '%file' => drupal_get_path('module', 'error_test') . '/error_test.module',
); );
$error_renderer_exception = array( $error_renderer_exception = array(
'%type' => 'Exception', '%type' => 'Exception',
'!message' => 'This is an exception that occurs during rendering', '@message' => 'This is an exception that occurs during rendering',
'%function' => 'Drupal\error_test\Controller\ErrorTestController->Drupal\error_test\Controller\{closure}()', '%function' => 'Drupal\error_test\Controller\ErrorTestController->Drupal\error_test\Controller\{closure}()',
'%line' => 82, '%line' => 82,
'%file' => drupal_get_path('module', 'error_test') . '/error_test.module', '%file' => drupal_get_path('module', 'error_test') . '/error_test.module',
...@@ -153,9 +159,9 @@ function testExceptionHandler() { ...@@ -153,9 +159,9 @@ function testExceptionHandler() {
// We cannot use assertErrorMessage() since the exact error reported // We cannot use assertErrorMessage() since the exact error reported
// varies from database to database. Check that the SQL string is displayed. // varies from database to database. Check that the SQL string is displayed.
$this->assertText($error_pdo_exception['%type'], format_string('Found %type in error page.', $error_pdo_exception)); $this->assertText($error_pdo_exception['%type'], format_string('Found %type in error page.', $error_pdo_exception));
$this->assertText($error_pdo_exception['!message'], format_string('Found !message in error page.', $error_pdo_exception)); $this->assertText($error_pdo_exception['@message'], format_string('Found @message in error page.', $error_pdo_exception));
$error_details = format_string('in %function (line ', $error_pdo_exception); $error_details = format_string('in %function (line ', $error_pdo_exception);
$this->assertRaw($error_details, format_string("Found '!message' in error page.", array('!message' => $error_details))); $this->assertRaw($error_details, format_string("Found '@message' in error page.", array('@message' => $error_details)));
$this->drupalGet('error-test/trigger-renderer-exception'); $this->drupalGet('error-test/trigger-renderer-exception');
$this->assertTrue(strpos($this->drupalGetHeader(':status'), '500 Service unavailable (with message)'), 'Received expected HTTP status line.'); $this->assertTrue(strpos($this->drupalGetHeader(':status'), '500 Service unavailable (with message)'), 'Received expected HTTP status line.');
...@@ -181,16 +187,16 @@ function testExceptionHandler() { ...@@ -181,16 +187,16 @@ function testExceptionHandler() {
* Helper function: assert that the error message is found. * Helper function: assert that the error message is found.
*/ */
function assertErrorMessage(array $error) { function assertErrorMessage(array $error) {
$message = t('%type: !message in %function (line ', $error); $message = t('%type: @message in %function (line ', $error);
$this->assertRaw($message, format_string('Found error message: !message.', array('!message' => $message))); $this->assertRaw($message, format_string('Found error message: @message.', array('@message' => $message)));
} }
/** /**
* Helper function: assert that the error message is not found. * Helper function: assert that the error message is not found.
*/ */
function assertNoErrorMessage(array $error) { function assertNoErrorMessage(array $error) {
$message = t('%type: !message in %function (line ', $error); $message = t('%type: @message in %function (line ', $error);
$this->assertNoRaw($message, format_string('Did not find error message: !message.', array('!message' => $message))); $this->assertNoRaw($message, format_string('Did not find error message: @message.', array('@message' => $message)));
} }
/** /**
......
...@@ -24,7 +24,7 @@ class TestLog implements LoggerInterface { ...@@ -24,7 +24,7 @@ class TestLog implements LoggerInterface {
public function log($level, $message, array $context = array()) { public function log($level, $message, array $context = array()) {
$trigger = [ $trigger = [
'%type' => 'Exception', '%type' => 'Exception',
'!message' => 'Deforestation', '@message' => 'Deforestation',
'%function' => 'Drupal\error_service_test\MonkeysInTheControlRoom->handle()', '%function' => 'Drupal\error_service_test\MonkeysInTheControlRoom->handle()',
'severity_level' => 3, 'severity_level' => 3,
'channel' => 'php', 'channel' => 'php',
......