diff -u b/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
--- b/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -514,7 +514,7 @@
* drupal_set_message(t('An error occurred and processing did not complete.'), 'error');
* @endcode
*
- * @param string|array $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. A render array may also be provided for markup.
@@ -526,7 +526,10 @@
* - 'error'
* @param bool $repeat
* (optional) If this is FALSE and the message is already set, then the
- * message won't be repeated. Defaults to FALSE.
+ * message won't be repeated. Defaults to FALSE. SafeStrings and strings which
+ * are equivalent will not be repeated as the in_array() comparison is not
+ * strict. However, if a string is passed once as a render array and once as
+ * string or SafeString this will repeated regardless of this setting.
*
* @return array|null
* A multidimensional array with keys corresponding to the set message types.
@@ -562,9 +565,13 @@
}
$new = array(
- 'safe' => (!is_array($message) && SafeMarkup::isSafe($message)),
+ // Only preserve safeness for strings. Objects and render arrays do not
+ // need to be marked as safe.
+ 'safe' => (is_string($message) && SafeMarkup::isSafe($message)),
'message' => $message,
);
+ // Do not use strict type checking so that equivalent string and
+ // SafeStringInterface objects are detected.
if ($repeat || !in_array($new, $_SESSION['messages'][$type])) {
$_SESSION['messages'][$type][] = $new;
}
diff -u b/core/modules/system/src/Tests/Bootstrap/DrupalSetMessageTest.php b/core/modules/system/src/Tests/Bootstrap/DrupalSetMessageTest.php
--- b/core/modules/system/src/Tests/Bootstrap/DrupalSetMessageTest.php
+++ b/core/modules/system/src/Tests/Bootstrap/DrupalSetMessageTest.php
@@ -24,31 +24,40 @@
public static $modules = array('system_test');
/**
- * Tests setting messages and removing one before it is displayed.
+ * Tests drupal_set_message().
*/
- function testSetRemoveMessages() {
+ function testDrupalSetMessage() {
// The page at system-test/drupal-set-message sets two messages and then
// removes the first before it is displayed.
$this->drupalGet('system-test/drupal-set-message');
$this->assertNoText('First message (removed).');
$this->assertRaw(t('Second message with markup! (not removed).'));
- }
- /**
- * Tests setting duplicated messages.
- */
- function testDuplicatedMessages() {
- $this->drupalGet('system-test/drupal-set-message');
+ // Ensure duplicate messages are handled as expected.
$this->assertUniqueText('Non Duplicated message');
$this->assertNoUniqueText('Duplicated message');
- }
- /**
- * Tests setting render array messages.
- */
- function testRenderArrayMessages() {
- $this->drupalGet('system-test/drupal-set-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!');
+ $this->assertRaw('SafeString2 with markup!');
+
+ // Ensure when the same message is of different types it is not duplicated.
+ $this->assertUniqueText('Non duplicate SafeString / string.');
+ $this->assertNoUniqueText('Duplicate SafeString / string.');
+
+ // Render arrays and strings which are equivalent can not be de-duplicated.
+ // Test this known shortcoming.
+ $this->assertNoUniqueText('Duplicate render array / string');
}
}
diff -u b/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php b/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php
--- b/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php
+++ b/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php
@@ -10,6 +10,7 @@
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Render\RendererInterface;
+use Drupal\Core\Render\SafeString;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Symfony\Component\HttpFoundation\RedirectResponse;
@@ -104,7 +105,4 @@
drupal_set_message(t('Second message with markup! (not removed).'));
- // Add a render array message.
- drupal_set_message(['#markup' => 'Render array with markup!']);
-
// Remove the first.
unset($_SESSION['messages']['status'][0]);
@@ -116,6 +114,37 @@
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.
+ drupal_set_message(SafeString::create('SafeString with markup!'));
+ // Ensure that multiple SafeString messages work.
+ drupal_set_message(SafeString::create('SafeString2 with markup!'));
+
+ // Test mixing of types.
+ drupal_set_message(SafeString::create('Non duplicate SafeString / string.'));
+ 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);
+
+ // This mixing of types will cause a duplicate even though it is not
+ // intended.
+ drupal_set_message(['#markup' => 'Duplicate render array / string']);
+ drupal_set_message('Duplicate render array / string');
+
return [];
}