diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 5300d76..ced9c4c 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -514,10 +514,12 @@ function watchdog_exception($type, Exception $exception, $message = NULL, $varia
* drupal_set_message(t('An error occurred and processing did not complete.'), 'error');
* @endcode
*
- * @param string|\Drupal\Component\Utility\SafeStringInterface $message
+ * @param string|array|\Drupal\Component\Utility\SafeStringInterface $message
* (optional) The translated message to be displayed to the user. For
* consistency with other messages, it should begin with a capital letter and
- * end with a period.
+ * end with a period. A render array may also be provided for markup, but
+ * #attached is not supported. Additionally, any cache tags and contexts are
+ * ignored, however this is okay because page caching is disabled.
* @param string $type
* (optional) The message's type. Defaults to 'status'. These values are
* supported:
@@ -566,6 +568,18 @@ function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE)
$message = \Drupal\Core\Render\SafeString::create($message);
}
+ // If the message is a render array, render it to a string now to avoid
+ // storing render arrays in $_SESSION, because rendering across requests
+ // may have unpredictable results and some render array functionality
+ // (like assets) should not be supported.
+ if (is_array($message)) {
+ $rendered_message = \Drupal::service('renderer')->renderPlain($message);
+ // The rendering will have all attachments bubbled to #attached in the top
+ // level of $message. Assert that there are none.
+ assert('empty($message[\'#attached\']);', 'Render arrays passed to drupal_set_message() cannot have attachments.');
+ $message = $rendered_message;
+ }
+
// Do not use strict type checking so that equivalent string and
// SafeStringInterface objects are detected.
if ($repeat || !in_array($message, $_SESSION['messages'][$type])) {
diff --git a/core/modules/system/src/Tests/Bootstrap/DrupalSetMessageTest.php b/core/modules/system/src/Tests/Bootstrap/DrupalSetMessageTest.php
index 1b63324..e54e30a 100644
--- a/core/modules/system/src/Tests/Bootstrap/DrupalSetMessageTest.php
+++ b/core/modules/system/src/Tests/Bootstrap/DrupalSetMessageTest.php
@@ -37,6 +37,15 @@ function testDrupalSetMessage() {
$this->assertUniqueText('Non Duplicated message');
$this->assertNoUniqueText('Duplicated message');
+ // Ensure render arrays are rendered.
+ $this->assertRaw('Render array with markup!');
+ $this->assertUniqueText('Render array with markup!');
+ $this->assertRaw('Render array 2 with markup!');
+
+ // Ensure render arrays with objects are rendered.
+ $this->assertRaw('Render array with SafeString markup!');
+ $this->assertUniqueText('Render array with SafeString markup!');
+
// Ensure SafeString objects are rendered as expected.
$this->assertRaw('SafeString with markup!');
$this->assertUniqueText('SafeString with markup!');
@@ -45,9 +54,13 @@ function testDrupalSetMessage() {
// Ensure when the same message is of different types it is not duplicated.
$this->assertUniqueText('Non duplicate SafeString / string.');
$this->assertNoUniqueText('Duplicate SafeString / string.');
+ $this->assertUniqueText('Duplicate render array / string');
// Ensure that strings that are not marked as safe are escaped.
$this->assertEscaped('Thismarkup will be escaped.');
+
+ // Ensure the runtime assertion fires on cue.
+ $this->assertUniqueText('Correct runtime assertion thrown when render array contains #attached.');
}
}
diff --git a/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php b/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php
index 15ab478..f4a566f 100644
--- a/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php
+++ b/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php
@@ -114,6 +114,18 @@ public function drupalSetMessageTest() {
drupal_set_message('Duplicated message', 'status', TRUE);
drupal_set_message('Duplicated message', 'status', TRUE);
+ // Add a render array message.
+ drupal_set_message(['#markup' => 'Render array with markup!']);
+ // Test duplicate render arrays.
+ drupal_set_message(['#markup' => 'Render array with markup!']);
+ // Ensure that multiple render array messages work.
+ drupal_set_message(['#markup' => 'Render array 2 with markup!']);
+
+ // Add a render array message containing objects.
+ drupal_set_message(['#markup' => SafeString::create('Render array with SafeString markup!')]);
+ // Test duplicate render array messages.
+ drupal_set_message(['#markup' => SafeString::create('Render array with SafeString markup!')]);
+
// Add a SafeString message.
drupal_set_message(SafeString::create('SafeString with markup!'));
// Test duplicate SafeString messages.
@@ -126,10 +138,41 @@ public function drupalSetMessageTest() {
drupal_set_message('Non duplicate SafeString / string.');
drupal_set_message(SafeString::create('Duplicate SafeString / string.'), 'status', TRUE);
drupal_set_message('Duplicate SafeString / string.', 'status', TRUE);
+ drupal_set_message(['#markup' => 'Duplicate render array / string']);
+ drupal_set_message('Duplicate render array / string');
// Test auto-escape of non safe strings.
drupal_set_message('Thismarkup will be escaped.');
+ // Test setting messages with a render array containing #attached.
+
+ // @todo Remove this after https://www.drupal.org/node/2536560 is finished.
+ assert_options(ASSERT_ACTIVE, TRUE);
+ assert_options(ASSERT_CALLBACK, function($file, $line, $code, $message) {
+ throw new \Exception($message);
+ });
+ // End block to be removed.
+
+ $array_with_attachments['foo']['bar']['#markup'] = t('Render array with assets');
+ $array_with_attachments['foo']['bar']['#attached']['drupalSettings'] = ['pecary' => TRUE];
+
+ try {
+ drupal_set_message($array_with_attachments);
+ }
+ catch (\Exception $e) {
+ // @todo Switch to next line when #2536560 is finished.
+ # catch (\AssertionError $e) {
+
+ // If the exception is the not the one we expect, rethrow causing a test
+ // failure.
+ if ($e->getMessage() !== 'Render arrays passed to drupal_set_message() cannot have attachments.') {
+ throw $e;
+ }
+ else {
+ drupal_set_message('Correct runtime assertion thrown when render array contains #attached.');
+ }
+ }
+
return [];
}