diff --git a/core/core.services.yml b/core/core.services.yml
index 459503e44c..49b27089b5 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1646,5 +1646,5 @@ services:
tags:
- { name: event_subscriber }
messenger:
- class: Drupal\Core\Messenger\LegacyMessenger
- arguments: ['@page_cache_kill_switch']
+ class: Drupal\Core\Messenger\Messenger
+ arguments: ['@session.flash_bag', '@page_cache_kill_switch']
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 6c8ee5e5f2..22b49234c8 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -455,9 +455,13 @@ function watchdog_exception($type, Exception $exception, $message = NULL, $varia
*
* @see drupal_get_messages()
* @see status-messages.html.twig
+ * @see https://www.drupal.org/node/2774931
+ *
+ * @deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0.
+ * Use \Drupal\Core\Messenger\MessengerInterface::addMessage() instead.
*/
function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE) {
- /* @var \Drupal\Core\Messenger\MessengerInterface $messenger */
+ @trigger_error('drupal_set_message() is deprecated Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Messenger\MessengerInterface::addMessage() instead. See https://www.drupal.org/node/2774931', E_USER_DEPRECATED);
$messenger = \Drupal::service('messenger');
$messenger->addMessage($message, $type, $repeat);
return $messenger->all();
@@ -487,11 +491,16 @@ function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE)
*
* @see drupal_set_message()
* @see status-messages.html.twig
+ * @see https://www.drupal.org/node/2774931
+ *
+ * @deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0.
+ * Use \Drupal\Core\Messenger\MessengerInterface::all() or
+ * \Drupal\Core\Messenger\MessengerInterface::messagesByType() instead.
*/
function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
- /** @var \Drupal\Core\Messenger\MessengerInterface $messenger */
- $messenger = \Drupal::hasService('messenger') ? \Drupal::service('messenger') : NULL;
- if ($messenger && ($messages = $messenger->all())) {
+ @trigger_error('drupal_get_message() is deprecated Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Messenger\MessengerInterface::all() or \Drupal\Core\Messenger\MessengerInterface::messagesByType() instead. See https://www.drupal.org/node/2774931', E_USER_DEPRECATED);
+ $messenger = \Drupal::messenger();
+ if ($messages = $messenger->all()) {
if ($type) {
if ($clear_queue) {
$messenger->deleteByType($type);
diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php
index 07dc12f1f3..825d2730d0 100644
--- a/core/lib/Drupal.php
+++ b/core/lib/Drupal.php
@@ -6,8 +6,9 @@
*/
use Drupal\Core\DependencyInjection\ContainerNotInitializedException;
-use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Messenger\ChainedMessenger;
use Drupal\Core\Url;
+use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Static Service Container wrapper.
@@ -100,6 +101,13 @@ class Drupal {
*/
protected static $container;
+ /**
+ * The messenger.
+ *
+ * @var \Drupal\Core\Messenger\MessengerInterface
+ */
+ protected static $messenger;
+
/**
* Sets a new global container.
*
@@ -757,4 +765,17 @@ public static function time() {
return static::getContainer()->get('datetime.time');
}
+ /**
+ * Returns the messenger.
+ *
+ * @return \Drupal\Core\Messenger\MessengerInterface
+ * The messenger.
+ */
+ public static function messenger() {
+ if (static::$messenger === NULL) {
+ static::$messenger = new ChainedMessenger();
+ }
+ return static::$messenger;
+ }
+
}
diff --git a/core/lib/Drupal/Core/Messenger/BaseMessenger.php b/core/lib/Drupal/Core/Messenger/BaseMessenger.php
new file mode 100644
index 0000000000..f76c295301
--- /dev/null
+++ b/core/lib/Drupal/Core/Messenger/BaseMessenger.php
@@ -0,0 +1,52 @@
+addMessage($message, static::TYPE_STATUS);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addError($message, $repeat = FALSE) {
+ return $this->addMessage($message, static::TYPE_ERROR);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addWarning($message, $repeat = FALSE) {
+ return $this->addMessage($message, static::TYPE_WARNING);
+ }
+
+ /**
+ * Convert any safe strings to markup.
+ *
+ * @param string|\Drupal\Component\Render\MarkupInterface $message
+ * The message.
+ *
+ * @return string|\Drupal\Component\Render\MarkupInterface
+ * The markup.
+ */
+ protected function convertToMarkup($message) {
+ if (!($message instanceof Markup) && $message instanceof MarkupInterface) {
+ $message = Markup::create((string) $message);
+ }
+ return $message;
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Messenger/ChainedMessenger.php b/core/lib/Drupal/Core/Messenger/ChainedMessenger.php
new file mode 100644
index 0000000000..b056a2ba7d
--- /dev/null
+++ b/core/lib/Drupal/Core/Messenger/ChainedMessenger.php
@@ -0,0 +1,134 @@
+memoryMessenger = new MemoryMessenger();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addMessage($message, $type = self::TYPE_STATUS, $repeat = FALSE) {
+ return $this->getMessenger()->addMessage($message, $type, $repeat);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addStatus($message, $repeat = FALSE) {
+ return $this->getMessenger()->addStatus($message, $repeat);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addError($message, $repeat = FALSE) {
+ return $this->getMessenger()->addError($message, $repeat);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addWarning($message, $repeat = FALSE) {
+ return $this->getMessenger()->addWarning($message, $repeat);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all() {
+ return $this->getMessenger()->all();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function messagesByType($type) {
+ return $this->getMessenger()->messagesByType($type);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function deleteAll() {
+ return $this->getMessenger()->deleteAll();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function deleteByType($type) {
+ return $this->getMessenger()->deleteByType($type);
+ }
+
+ /**
+ * Get the messenger to use.
+ *
+ * @return \Drupal\Core\Messenger\MessengerInterface
+ * The messenger.
+ */
+ protected function getMessenger() {
+ // Shortcut if we have a messenger service.
+ if (isset($this->messengerService)) {
+ return $this->messengerService;
+ }
+
+ // If the service exists, but we haven't set it yet.
+ if (\Drupal::hasService('messenger')) {
+ $this->messengerService = \Drupal::service('messenger');
+ $this->mergeMessages($this->memoryMessenger, $this->messengerService);
+ return $this->messengerService;
+ }
+
+ // Fallback to memory messenger.
+ return $this->memoryMessenger;
+ }
+
+ /**
+ * Merges messages from one messenger to another.
+ *
+ * @param \Drupal\Core\Messenger\MessengerInterface $from
+ * The messenger to merge from.
+ * @param \Drupal\Core\Messenger\MessengerInterface $to
+ * The messenger to merge to.
+ */
+ public function mergeMessages(MessengerInterface $from, MessengerInterface $to) {
+ foreach ($from->all() as $type => $messages) {
+ foreach ($messages as $message) {
+ $to->addMessage($message, $type);
+ }
+ }
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Messenger/LegacyMessenger.php b/core/lib/Drupal/Core/Messenger/LegacyMessenger.php
deleted file mode 100644
index 8c9751f3d8..0000000000
--- a/core/lib/Drupal/Core/Messenger/LegacyMessenger.php
+++ /dev/null
@@ -1,212 +0,0 @@
-killSwitch = $killSwitch;
- }
-
- /**
- * {@inheritdoc}
- */
- public function addMessage($message, $type = self::TYPE_STATUS, $repeat = FALSE) {
- $this->setMessage($message, $type, $repeat);
- }
-
- /**
- * {@inheritdoc}
- */
- public function addStatus($message, $repeat = FALSE) {
- return $this->addMessage($message, static::TYPE_STATUS);
- }
-
- /**
- * {@inheritdoc}
- */
- public function addError($message, $repeat = FALSE) {
- return $this->addMessage($message, static::TYPE_ERROR);
- }
-
- /**
- * {@inheritdoc}
- */
- public function addWarning($message, $repeat = FALSE) {
- return $this->addMessage($message, static::TYPE_WARNING);
- }
-
- /**
- * {@inheritdoc}
- */
- public function all() {
- return $this->getMessages(NULL, FALSE);
- }
-
- /**
- * {@inheritdoc}
- */
- public function messagesByType($type) {
- return $this->getMessages($type, FALSE);
- }
-
- /**
- * {@inheritdoc}
- */
- public function deleteAll() {
- return $this->getMessages(NULL, TRUE);
- }
-
- /**
- * {@inheritdoc}
- */
- public function deleteByType($type) {
- return $this->getMessages($type, TRUE);
- }
-
- /**
- * Sets a message to display to the user.
- *
- * Messages are stored in a session variable and displayed in the page template
- * via the $messages theme variable.
- *
- * Example usage:
- * @code
- * drupal_set_message(t('An error occurred and processing did not complete.'), 'error');
- * @endcode
- *
- * @param string|\Drupal\Component\Render\MarkupInterface $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.
- * @param string $type
- * (optional) The message's type. Defaults to 'status'. These values are
- * supported:
- * - 'status'
- * - 'warning'
- * - '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.
- *
- * @return array|null
- * A multidimensional array with keys corresponding to the set message types.
- * The indexed array values of each contain the set messages for that type,
- * and each message is an associative array with the following format:
- * - safe: Boolean indicating whether the message string has been marked as
- * safe. Non-safe strings will be escaped automatically.
- * - message: The message string.
- * So, the following is an example of the full return array structure:
- * @code
- * array(
- * 'status' => array(
- * array(
- * 'safe' => TRUE,
- * 'message' => 'A safe markup string.',
- * ),
- * array(
- * 'safe' => FALSE,
- * 'message' => "$arbitrary_user_input to escape.",
- * ),
- * ),
- * );
- * @endcode
- * If there are no messages set, the function returns NULL.
- *
- * @internal
- */
- private function setMessage($message = NULL, $type = 'status', $repeat = FALSE) {
- if (isset($message)) {
- if (!isset($_SESSION['messages'][$type])) {
- $_SESSION['messages'][$type] = [];
- }
-
- // Convert strings which are safe to the simplest Markup objects.
- if (!($message instanceof Markup) && $message instanceof MarkupInterface) {
- $message = Markup::create((string) $message);
- }
-
- // Do not use strict type checking so that equivalent string and
- // MarkupInterface objects are detected.
- if ($repeat || !in_array($message, $_SESSION['messages'][$type])) {
- $_SESSION['messages'][$type][] = $message;
- }
-
- // Mark this page as being uncacheable.
- $this->killSwitch->trigger();
- }
-
- // Messages not set when DB connection fails.
- return isset($_SESSION['messages']) ? $_SESSION['messages'] : NULL;
- }
-
- /**
- * Returns all messages that have been set with drupal_set_message().
- *
- * @param string $type
- * (optional) Limit the messages returned by type. Defaults to NULL, meaning
- * all types. These values are supported:
- * - NULL
- * - 'status'
- * - 'warning'
- * - 'error'
- * @param bool $clear_queue
- * (optional) If this is TRUE, the queue will be cleared of messages of the
- * type specified in the $type parameter. Otherwise the queue will be left
- * intact. Defaults to TRUE.
- *
- * @return array
- * An associative, nested array of messages grouped by message type, with
- * the top-level keys as the message type. The messages returned are
- * limited to the type specified in the $type parameter, if any. If there
- * are no messages of the specified type, an empty array is returned. See
- * drupal_set_message() for the array structure of individual messages.
- *
- * @see drupal_set_message()
- * @see status-messages.html.twig
- *
- * @internal
- */
- private function getMessages($type = NULL, $clear_queue = TRUE) {
- if ($messages = $this->setMessage()) {
- if ($type) {
- if ($clear_queue) {
- unset($_SESSION['messages'][$type]);
- }
- if (isset($messages[$type])) {
- return [$type => $messages[$type]];
- }
- }
- else {
- if ($clear_queue) {
- unset($_SESSION['messages']);
- }
- return $messages;
- }
- }
- return [];
- }
-
-}
diff --git a/core/lib/Drupal/Core/Messenger/MemoryMessenger.php b/core/lib/Drupal/Core/Messenger/MemoryMessenger.php
new file mode 100644
index 0000000000..494a23a8e5
--- /dev/null
+++ b/core/lib/Drupal/Core/Messenger/MemoryMessenger.php
@@ -0,0 +1,71 @@
+ [],
+ MessengerInterface::TYPE_STATUS => [],
+ MessengerInterface::TYPE_WARNING => [],
+ ];
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addMessage($message, $type = self::TYPE_STATUS, $repeat = FALSE) {
+ $message = $this->convertToMarkup($message);
+ // Do not use strict type checking so that equivalent string and
+ // MarkupInterface objects are detected.
+ if ($repeat || !in_array($message, $this->messages[$type])) {
+ $this->messages[$type][] = $message;
+ }
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all() {
+ return $this->messages;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function messagesByType($type) {
+ return $this->messages[$type];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function deleteAll() {
+ $messages = $this->messages;
+ $this->messages = [];
+ return $messages;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function deleteByType($type) {
+ $messages = $this->messages[$type];
+ $this->messages[$type] = [];
+ return $messages;
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Messenger/Messenger.php b/core/lib/Drupal/Core/Messenger/Messenger.php
new file mode 100644
index 0000000000..a846defd01
--- /dev/null
+++ b/core/lib/Drupal/Core/Messenger/Messenger.php
@@ -0,0 +1,88 @@
+flashBag = $flash_bag;
+ $this->killSwitch = $killSwitch;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addMessage($message, $type = self::TYPE_STATUS, $repeat = FALSE) {
+ $message = $this->convertToMarkup($message);
+ // Do not use strict type checking so that equivalent string and
+ // MarkupInterface objects are detected.
+ if ($repeat || !in_array($message, $this->flashBag->peek($type))) {
+ $this->flashBag->add($type, $message);
+ }
+
+ // Mark this page as being uncacheable.
+ $this->killSwitch->trigger();
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all() {
+ return $this->flashBag->peekAll();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function messagesByType($type) {
+ return $this->flashBag->peek($type);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function deleteAll() {
+ return $this->flashBag->clear();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function deleteByType($type) {
+ // Flash bag gets and clears flash messages from the stack.
+ return $this->flashBag->get($type);
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Messenger/MessengerInterface.php b/core/lib/Drupal/Core/Messenger/MessengerInterface.php
index 216835bcea..809c93d118 100644
--- a/core/lib/Drupal/Core/Messenger/MessengerInterface.php
+++ b/core/lib/Drupal/Core/Messenger/MessengerInterface.php
@@ -109,11 +109,15 @@ public function all();
* or self::TYPE_ERROR.
*
* @return string[]|\Drupal\Component\Render\MarkupInterface[]
+ * The messages of given type.
*/
public function messagesByType($type);
/**
* Deletes all messages.
+ *
+ * @return string[]|\Drupal\Component\Render\MarkupInterface[]
+ * The deleted messages.
*/
public function deleteAll();
@@ -123,6 +127,9 @@ public function deleteAll();
* @param string $type
* The messages' type. Either self::TYPE_STATUS, self::TYPE_WARNING, or
* self::TYPE_ERROR.
+ *
+ * @return string[]|\Drupal\Component\Render\MarkupInterface[]
+ * The deleted messages of given type..
*/
public function deleteByType($type);
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 37eb87585d..e350bc2eaf 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
@@ -5,6 +5,7 @@
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Cache\CacheableResponse;
use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Render\Markup;
use Drupal\Core\Session\AccountInterface;
@@ -48,6 +49,13 @@ class SystemTestController extends ControllerBase {
*/
protected $renderer;
+ /**
+ * The messenger service.
+ *
+ * @var \Drupal\Core\Messenger\MessengerInterface
+ */
+ protected $messenger;
+
/**
* Constructs the SystemTestController.
*
@@ -59,12 +67,15 @@ class SystemTestController extends ControllerBase {
* The current user.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer.
+ * @param \Drupal\Core\Messenger\MessengerInterface $messenger
+ * The messenger service.
*/
- public function __construct(LockBackendInterface $lock, LockBackendInterface $persistent_lock, AccountInterface $current_user, RendererInterface $renderer) {
+ public function __construct(LockBackendInterface $lock, LockBackendInterface $persistent_lock, AccountInterface $current_user, RendererInterface $renderer, MessengerInterface $messenger) {
$this->lock = $lock;
$this->persistentLock = $persistent_lock;
$this->currentUser = $current_user;
$this->renderer = $renderer;
+ $this->messenger = $messenger;
}
/**
@@ -75,7 +86,8 @@ public static function create(ContainerInterface $container) {
$container->get('lock'),
$container->get('lock.persistent'),
$container->get('current_user'),
- $container->get('renderer')
+ $container->get('renderer'),
+ $container->get('messenger')
);
}
@@ -99,9 +111,13 @@ public function drupalSetMessageTest() {
// Set two messages.
drupal_set_message('First message (removed).');
drupal_set_message(t('Second message with markup! (not removed).'));
-
+ $messages = $this->messenger->deleteByType('status');
// Remove the first.
- unset($_SESSION['messages']['status'][0]);
+ unset($messages[0]);
+
+ foreach ($messages as $message) {
+ $this->messenger->addStatus($message);
+ }
// Duplicate message check.
drupal_set_message('Non Duplicated message', 'status', FALSE);
diff --git a/core/tests/Drupal/KernelTests/Core/Common/DrupalSetMessageTest.php b/core/tests/Drupal/KernelTests/Core/Common/DrupalSetMessageTest.php
index 59470e6563..7a15fc3e04 100644
--- a/core/tests/Drupal/KernelTests/Core/Common/DrupalSetMessageTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Common/DrupalSetMessageTest.php
@@ -20,10 +20,4 @@ public function testDrupalSetMessage() {
$this->assertEquals('A message: bar', (string) $messages['status'][0]);
}
- protected function tearDown() {
- // Clear session to prevent global leakage.
- unset($_SESSION['messages']);
- parent::tearDown();
- }
-
}
diff --git a/core/tests/Drupal/Tests/Core/Messenger/ChainedMessengerTest.php b/core/tests/Drupal/Tests/Core/Messenger/ChainedMessengerTest.php
new file mode 100644
index 0000000000..d79e50ae74
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Messenger/ChainedMessengerTest.php
@@ -0,0 +1,41 @@
+addWarning("Foo");
+ $messengerFrom->addStatus("Bar");
+ $messengerFrom->addError("Fiz");
+
+ $messengerTo = new MemoryMessenger();
+ $messengerTo->addWarning("Platypus");
+
+ $chainedMessenger = new ChainedMessenger();
+ $chainedMessenger->mergeMessages($messengerFrom, $messengerTo);
+
+ $messages = $messengerTo->all();
+
+ $this->assertContains("Platypus", $messages[MessengerInterface::TYPE_WARNING]);
+ $this->assertContains("Foo", $messages[MessengerInterface::TYPE_WARNING]);
+ $this->assertContains("Bar", $messages[MessengerInterface::TYPE_STATUS]);
+ $this->assertContains("Fiz", $messages[MessengerInterface::TYPE_ERROR]);
+
+ }
+
+}
diff --git a/core/tests/Drupal/Tests/Listeners/DeprecationListener.php b/core/tests/Drupal/Tests/Listeners/DeprecationListener.php
index 80b3d31c97..99f133480f 100644
--- a/core/tests/Drupal/Tests/Listeners/DeprecationListener.php
+++ b/core/tests/Drupal/Tests/Listeners/DeprecationListener.php
@@ -113,6 +113,8 @@ public static function getSkippedDeprecations() {
'Automatically creating the first item for computed fields is deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.0. Use \Drupal\Core\TypedData\ComputedItemListTrait instead.',
'"\Drupal\Core\Entity\ContentEntityStorageBase::doLoadRevisionFieldItems()" is deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.0. "\Drupal\Core\Entity\ContentEntityStorageBase::doLoadMultipleRevisionsFieldItems()" should be implemented instead. See https://www.drupal.org/node/2924915.',
'Passing a single revision ID to "\Drupal\Core\Entity\Sql\SqlContentEntityStorage::buildQuery()" is deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.0. An array of revision IDs should be given instead. See https://www.drupal.org/node/2924915.',
+ 'drupal_set_message() is deprecated Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Messenger\MessengerInterface::addMessage() instead. See https://www.drupal.org/node/2774931',
+ 'drupal_get_message() is deprecated Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Messenger\MessengerInterface::all() or \Drupal\Core\Messenger\MessengerInterface::messagesByType() instead. See https://www.drupal.org/node/2774931',
];
}