diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 9849d43..2195ffe 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -488,10 +488,7 @@ function watchdog_exception($type, Exception $exception, $message = NULL, $varia // Use a default value if $message is not set. if (empty($message)) { - // The exception message is run through - // \Drupal\Component\Utility\SafeMarkup::checkPlain() by - // \Drupal\Core\Utility\Error:decodeException(). - $message = '%type: !message in %function (line %line of %file).'; + $message = '%type: @message in %function (line %line of %file).'; } if ($link) { diff --git a/core/includes/errors.inc b/core/includes/errors.inc index 6933dc8..c41c688 100644 --- a/core/includes/errors.inc +++ b/core/includes/errors.inc @@ -68,7 +68,7 @@ function _drupal_error_handler_real($error_level, $message, $filename, $line, $c '%type' => isset($types[$error_level]) ? $severity_msg : 'Unknown error', // The standard PHP error handler considers that the error messages // are HTML. We mimick this behavior here. - '!message' => Xss::filterAdmin($message), + '@message' => Xss::filterAdmin($message), '%function' => $caller['function'], '%file' => $caller['file'], '%line' => $caller['line'], @@ -109,9 +109,9 @@ function error_displayable($error = NULL) { * Logs a PHP error or exception and displays an error page in fatal cases. * * @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, - * 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. * @param $fatal * TRUE if the error is fatal. @@ -145,7 +145,7 @@ function _drupal_log_error($error, $fatal = FALSE) { // as it uniquely identifies each PHP error. static $number = 0; $assertion = array( - $error['!message'], + $error['@message'], $error['%type'], array( 'function' => $error['%function'], @@ -161,14 +161,14 @@ function _drupal_log_error($error, $fatal = FALSE) { // if there is an error while rebuilding the container or during the // installer. if (\Drupal::hasService('logger.factory')) { - \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); } if (PHP_SAPI === 'cli') { if ($fatal) { // When called from CLI, simply output a plain text message. // Should not translate the string to avoid errors producing more errors. - print html_entity_decode(strip_tags(format_string('%type: !message in %function (line %line of %file).', $error))). "\n"; + print html_entity_decode(strip_tags(format_string('%type: @message in %function (line %line of %file).', $error))). "\n"; exit; } } @@ -178,7 +178,7 @@ function _drupal_log_error($error, $fatal = FALSE) { if (error_displayable($error)) { // When called from JavaScript, simply output the error message. // Should not translate the string to avoid errors producing more errors. - print format_string('%type: !message in %function (line %line of %file).', $error); + print format_string('%type: @message in %function (line %line of %file).', $error); } exit; } @@ -205,12 +205,16 @@ function _drupal_log_error($error, $fatal = FALSE) { $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. $error_level = _drupal_get_error_level(); + // With verbose logging, we will append a backtrace. To keep our string + // safe, we need to include the
 tags inside the string to be
+      // formatted, and not included as part of the placeholder.
+      // 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.
       if ($error_level == ERROR_REPORTING_DISPLAY_VERBOSE) {
         // First trace is the error itself, already contained in the message.
         // While the second trace is the error source and also contained in the
@@ -218,11 +222,16 @@ function _drupal_log_error($error, $fatal = FALSE) {
         // once more in the backtrace.
         array_shift($backtrace);
         // Generate a backtrace containing only scalar argument values.
-        $message .= '
' . Error::formatBacktrace($backtrace) . '
'; + $error['@backtrace'] = Error::formatBacktrace($backtrace); + $message = SafeMarkup::format('%type: @message in %function (line %line of %file).
@backtrace
', $error); + } + else { + $message = SafeMarkup::format('%type: @message in %function (line %line of %file).', $error); } + if (\Drupal::hasService('session')) { // Message display is dependent on sessions being available. - drupal_set_message(SafeMarkup::set($message), $class, TRUE); + drupal_set_message($message, $class, TRUE); } else { print $message; diff --git a/core/includes/update.inc b/core/includes/update.inc index 9d4557f..e59abd0 100644 --- a/core/includes/update.inc +++ b/core/includes/update.inc @@ -191,7 +191,7 @@ function update_do_one($module, $number, $dependency_map, &$context) { // The exception message is run through // \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)); + $ret['#abort'] = array('success' => FALSE, 'query' => t('%type: @message in %function (line %line of %file).', $variables)); } } @@ -239,10 +239,7 @@ function update_entity_definitions($module, $number, &$context) { watchdog_exception('update', $e); $variables = Error::decodeException($e); unset($variables['backtrace']); - // The exception message is run through - // \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)); + $ret['#abort'] = array('success' => FALSE, 'query' => t('%type: @message in %function (line %line of %file).', $variables)); $context['results'][$module][$number] = $ret; $context['results']['#abort'][] = 'update_entity_definitions'; } diff --git a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php index e3ec321..770b96f 100644 --- a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php @@ -161,7 +161,7 @@ protected function makeSubrequest(GetResponseForExceptionEvent $event, $url, $st // just log it. The DefaultExceptionSubscriber will catch the original // exception and handle it normally. $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); } } } diff --git a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php index 883927c..7438a81 100644 --- a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php @@ -104,12 +104,18 @@ protected function onHtml(GetResponseForExceptionEvent $event) { if (substr($error['%file'], 0, $root_length) == DRUPAL_ROOT) { $error['%file'] = substr($error['%file'], $root_length + 1); } - // Do not translate the string to avoid errors producing more errors. - 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) { + $error_level = _drupal_get_error_level(); + unset($error['backtrace']); + + // With verbose logging, we will append a backtrace. To keep our string + // safe, we need to include the
 tags inside the string to be
+      // formatted, and not included as part of the placeholder.
+      // 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.
+      if ($error_level == ERROR_REPORTING_DISPLAY_VERBOSE) {
         $backtrace_exception = $exception;
         while ($backtrace_exception->getPrevious()) {
           $backtrace_exception = $backtrace_exception->getPrevious();
@@ -120,12 +126,16 @@ protected function onHtml(GetResponseForExceptionEvent $event) {
         // message, the message doesn't contain argument values, so we output it
         // once more in the backtrace.
         array_shift($backtrace);
-
-        // Generate a backtrace containing only scalar argument values. Make
-        // sure the backtrace is escaped as it can contain user submitted data.
-        $message .= '
' . SafeMarkup::escape(Error::formatBacktrace($backtrace)) . '
'; + // Generate a backtrace containing only scalar argument values. + $error['@backtrace'] = Error::formatBacktrace($backtrace); + $message = SafeMarkup::format('%type: @message in %function (line %line of %file).
@backtrace
', $error); + } + else { + $message = SafeMarkup::format('%type: @message in %function (line %line of %file).', $error); } - drupal_set_message(SafeMarkup::set($message), $class, TRUE); + + // Do not translate the string to avoid errors producing more errors. + drupal_set_message($message, $class, TRUE); } $content = $this->t('The website encountered an unexpected error. Please try again later.'); diff --git a/core/lib/Drupal/Core/EventSubscriber/ExceptionLoggingSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ExceptionLoggingSubscriber.php index cb2fc31..6a2bd6c 100644 --- a/core/lib/Drupal/Core/EventSubscriber/ExceptionLoggingSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/ExceptionLoggingSubscriber.php @@ -68,7 +68,7 @@ public function on404(GetResponseForExceptionEvent $event) { public function onError(GetResponseForExceptionEvent $event) { $exception = $event->getException(); $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; if ($is_critical) { diff --git a/core/lib/Drupal/Core/EventSubscriber/ExceptionTestSiteSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ExceptionTestSiteSubscriber.php index d8f1a61..17c87a3 100644 --- a/core/lib/Drupal/Core/EventSubscriber/ExceptionTestSiteSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/ExceptionTestSiteSubscriber.php @@ -51,7 +51,7 @@ public function on500(GetResponseForExceptionEvent $event) { // as it uniquely identifies each PHP error. static $number = 0; $assertion = array( - $error['!message'], + $error['@message'], $error['%type'], array( 'function' => $error['%function'], diff --git a/core/lib/Drupal/Core/Utility/Error.php b/core/lib/Drupal/Core/Utility/Error.php index c72fba4..d150ec3 100644 --- a/core/lib/Drupal/Core/Utility/Error.php +++ b/core/lib/Drupal/Core/Utility/Error.php @@ -70,7 +70,7 @@ public static function decodeException($exception) { '%type' => get_class($exception), // The standard PHP exception handler considers that the exception message // is plain-text. We mimic this behavior here. - '!message' => SafeMarkup::checkPlain($message), + '@message' => $message, '%function' => $caller['function'], '%file' => $caller['file'], '%line' => $caller['line'], @@ -94,15 +94,14 @@ public static function renderExceptionSafe($exception) { unset($decode['backtrace']); // Remove 'main()'. array_shift($backtrace); + $decode['@backtrace'] = $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 // site, it is only called when the exception handler itself threw an // exception, which normally means that a code change caused the system to // no longer function correctly (as opposed to a user-triggered error), so // we assume that it is safe to include a verbose backtrace. - $output .= '
' . static::formatBacktrace($backtrace) . '
'; - return SafeMarkup::set($output); + return SafeMarkup::format('%type: @message in %function (line %line of %file).
@backtrace
', $decode); } /** diff --git a/core/modules/migrate/src/MigrateExecutable.php b/core/modules/migrate/src/MigrateExecutable.php index 139a07d..3331c83 100644 --- a/core/modules/migrate/src/MigrateExecutable.php +++ b/core/modules/migrate/src/MigrateExecutable.php @@ -617,7 +617,7 @@ protected function getTimeElapsed() { */ protected function handleException(\Exception $exception, $save = TRUE) { $result = Error::decodeException($exception); - $message = $result['!message'] . ' (' . $result['%file'] . ':' . $result['%line'] . ')'; + $message = $result['@message'] . ' (' . $result['%file'] . ':' . $result['%line'] . ')'; if ($save) { $this->saveMessage($message); } diff --git a/core/modules/simpletest/src/TestBase.php b/core/modules/simpletest/src/TestBase.php index 9078d39..209e646 100644 --- a/core/modules/simpletest/src/TestBase.php +++ b/core/modules/simpletest/src/TestBase.php @@ -1396,8 +1396,8 @@ protected function exceptionHandler($exception) { // message through \Drupal\Component\Utility\SafeMarkup::checkPlain(). $decoded_exception = Error::decodeException($exception); unset($decoded_exception['backtrace']); - $message = SafeMarkup::format('%type: !message in %function (line %line of %file).
!backtrace
', $decoded_exception + array( - '!backtrace' => Error::formatBacktrace($verbose_backtrace), + $message = SafeMarkup::format('%type: @message in %function (line %line of %file).
@backtrace
', $decoded_exception + array( + '@backtrace' => Error::formatBacktrace($verbose_backtrace), )); $this->error($message, 'Uncaught exception', Error::getLastCaller($backtrace)); } diff --git a/core/modules/system/src/Tests/System/ErrorHandlerTest.php b/core/modules/system/src/Tests/System/ErrorHandlerTest.php index a42686b..cf00d62 100644 --- a/core/modules/system/src/Tests/System/ErrorHandlerTest.php +++ b/core/modules/system/src/Tests/System/ErrorHandlerTest.php @@ -30,32 +30,32 @@ function testErrorHandler() { $config = $this->config('system.logging'); $error_notice = array( '%type' => 'Notice', - '!message' => 'Undefined variable: bananas', + '@message' => 'Undefined variable: bananas', '%function' => 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', '%file' => drupal_get_path('module', 'error_test') . '/error_test.module', ); $error_warning = array( '%type' => 'Warning', - '!message' => 'Division by zero', + '@message' => 'Division by zero', '%function' => 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', '%file' => drupal_get_path('module', 'error_test') . '/error_test.module', ); $error_user_notice = array( '%type' => 'User warning', - '!message' => 'Drupal is awesome', + '@message' => 'Drupal is awesome', '%function' => 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', '%file' => drupal_get_path('module', 'error_test') . '/error_test.module', ); $fatal_error = array( '%type' => 'Recoverable fatal error', '%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) { // 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'; + $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. @@ -123,21 +123,21 @@ function testExceptionHandler() { $error_exception = array( '%type' => 'Exception', - '!message' => 'Drupal is awesome', + '@message' => 'Drupal is awesome', '%function' => 'Drupal\error_test\Controller\ErrorTestController->triggerException()', '%line' => 56, '%file' => drupal_get_path('module', 'error_test') . '/error_test.module', ); $error_pdo_exception = array( '%type' => 'DatabaseExceptionWrapper', - '!message' => 'SELECT * FROM bananas_are_awesome', + '@message' => 'SELECT * FROM bananas_are_awesome', '%function' => 'Drupal\error_test\Controller\ErrorTestController->triggerPDOException()', '%line' => 64, '%file' => drupal_get_path('module', 'error_test') . '/error_test.module', ); $error_renderer_exception = array( '%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}()', '%line' => 82, '%file' => drupal_get_path('module', 'error_test') . '/error_test.module', @@ -152,9 +152,9 @@ function testExceptionHandler() { // We cannot use assertErrorMessage() since the exact error reported // 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['!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); - $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->assertTrue(strpos($this->drupalGetHeader(':status'), '500 Service unavailable (with message)'), 'Received expected HTTP status line.'); @@ -180,15 +180,16 @@ function testExceptionHandler() { * Helper function: assert that the error message is found. */ function assertErrorMessage(array $error) { - $message = t('%type: !message in %function (line ', $error); - $this->assertRaw($message, format_string('Found error message: !message.', array('!message' => $message))); + $message = t('%type: @message in %function (line ', $error); + debug($message); + $this->assertRaw($message, format_string('Found error message: @message.', array('@message' => $message))); } /** * Helper function: assert that the error message is not found. */ function assertNoErrorMessage(array $error) { - $message = t('%type: !message in %function (line ', $error); - $this->assertNoRaw($message, format_string('Did not find error message: !message.', array('!message' => $message))); + $message = t('%type: @message in %function (line ', $error); + $this->assertNoRaw($message, format_string('Did not find error message: @message.', array('@message' => $message))); } } diff --git a/sites/default/default.services.yml b/sites/default/default.services.yml index ecb1c43..a9072ca 100644 --- a/sites/default/default.services.yml +++ b/sites/default/default.services.yml @@ -25,9 +25,9 @@ parameters: cookie_lifetime: 2000000 # # Drupal automatically generates a unique session cookie name based on the - # full domain name used to access the site. This mechanism is sufficient - # for most use-cases, including multi-site deployments. However, if it is - # desired that a session can be reused across different subdomains, the + # full domain name used to access the site. This mechanism is sufficent for + # most use-cases, including multi-site deployments. However, if it is + # desired that a session can be reused accross different subdomains, the # cookie domain needs to be set to the shared base domain. Doing so assures # that users remain logged in as they cross between various subdomains. # To maximize compatibility and normalize the behavior across user agents,