From 126ebbb3c012c394845765302b82bdd15b0b9b22 Mon Sep 17 00:00:00 2001
From: Mark Carver <mark.carver@me.com>
Date: Mon, 4 Dec 2017 14:14:53 -0600
Subject: [PATCH] Issue #2760167 by kim.pepper, jibran, markcarver, znerol, Wim
 Leers, dawehner: Add \Drupal\Core\Messenger\Messenger and remove
 \Drupal\Core\Messenger\LegacyMessenger

---
 core/core.services.yml                             |   4 +-
 core/includes/bootstrap.inc                        |  19 +-
 core/lib/Drupal.php                                |  23 ++-
 core/lib/Drupal/Core/Messenger/BaseMessenger.php   |  52 +++++
 .../lib/Drupal/Core/Messenger/ChainedMessenger.php | 128 +++++++++++++
 core/lib/Drupal/Core/Messenger/LegacyMessenger.php | 212 ---------------------
 core/lib/Drupal/Core/Messenger/MemoryMessenger.php |  71 +++++++
 core/lib/Drupal/Core/Messenger/Messenger.php       |  88 +++++++++
 .../Drupal/Core/Messenger/MessengerInterface.php   |   7 +
 .../src/Controller/SystemTestController.php        |  24 ++-
 .../Core/Common/DrupalSetMessageTest.php           |   6 -
 .../Core/Messenger/ChainedMessengerTest.php        |  69 +++++++
 .../Drupal/Tests/Listeners/DeprecationListener.php |   2 +
 13 files changed, 475 insertions(+), 230 deletions(-)
 create mode 100644 core/lib/Drupal/Core/Messenger/BaseMessenger.php
 create mode 100644 core/lib/Drupal/Core/Messenger/ChainedMessenger.php
 delete mode 100644 core/lib/Drupal/Core/Messenger/LegacyMessenger.php
 create mode 100644 core/lib/Drupal/Core/Messenger/MemoryMessenger.php
 create mode 100644 core/lib/Drupal/Core/Messenger/Messenger.php
 create mode 100644 core/tests/Drupal/KernelTests/Core/Messenger/ChainedMessengerTest.php

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 d05298d4bf..35035ffa0a 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -466,10 +466,14 @@ 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 */
-  $messenger = \Drupal::service('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::messenger();
   $messenger->addMessage($message, $type, $repeat);
   return $messenger->all();
 }
@@ -498,11 +502,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 @@
+<?php
+
+namespace Drupal\Core\Messenger;
+
+use Drupal\Component\Render\MarkupInterface;
+use Drupal\Core\Render\Markup;
+
+/**
+ * Provides a base Messenger implementation.
+ *
+ * @internal
+ */
+abstract class BaseMessenger implements MessengerInterface {
+
+  /**
+   * {@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);
+  }
+
+  /**
+   * 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..f28de8d0b1
--- /dev/null
+++ b/core/lib/Drupal/Core/Messenger/ChainedMessenger.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace Drupal\Core\Messenger;
+
+/**
+ * Provides a chained Messenger.
+ *
+ * This implementation uses a memory messenger for handling messages before the
+ * container is initialized. When initialised, it will copy over messages and
+ * use that thereafter.
+ *
+ * You should not use this class directly. Instead use the 'messenger' service.
+ *
+ * @internal
+ */
+class ChainedMessenger implements MessengerInterface {
+
+  /**
+   * The memory messenger.
+   *
+   * @var \Drupal\Core\Messenger\MessengerInterface
+   */
+  protected $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.
+   */
+  public function getMessenger() {
+    // Use the Messenger service, if it exists. Note: because the container has
+    // the potential to be rebuilt during requests, this service cannot be
+    // stored on this class directly since it is statically cached in
+    // \Drupal::messenger().
+    if (\Drupal::hasService('messenger')) {
+      $messenger = \Drupal::service('messenger');
+
+      // Merge in any MemoryMessenger messages and then remove it.
+      if (isset($this->memoryMessenger)) {
+        $this->mergeMessages($this->memoryMessenger, $messenger);
+        unset($this->memoryMessenger);
+      }
+
+      return $messenger;
+    }
+
+    // Fallback to MemoryMessenger.
+    if (!isset($this->memoryMessenger)) {
+      $this->memoryMessenger = new MemoryMessenger();
+    }
+
+    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.
+   */
+  protected 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 @@
-<?php
-
-namespace Drupal\Core\Messenger;
-
-use Drupal\Component\Render\MarkupInterface;
-use Drupal\Core\PageCache\ResponsePolicy\KillSwitch;
-use Drupal\Core\Render\Markup;
-
-/**
- * A legacy implementation of the messenger interface.
- *
- * @internal
- */
-class LegacyMessenger implements MessengerInterface {
-
-  /**
-   * The page cache kill switch.
-   *
-   * @var \Drupal\Core\PageCache\ResponsePolicy\KillSwitch
-   */
-  protected $killSwitch;
-
-  /**
-   * LegacyMessenger constructor.
-   *
-   * @param \Drupal\Core\PageCache\ResponsePolicy\KillSwitch $killSwitch
-   *   (optional) The page cache kill switch.
-   */
-  public function __construct(KillSwitch $killSwitch) {
-    $this->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 <em>safe</em> 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 @@
+<?php
+
+namespace Drupal\Core\Messenger;
+
+/**
+ * Provides a Messenger implementation which stores messages in memory.
+ *
+ * Used for early the bootstrap phase when there is no service container.
+ *
+ * @internal
+ */
+class MemoryMessenger extends BaseMessenger {
+
+  /**
+   * The messages.
+   *
+   * @var array
+   */
+  protected $messages = [
+    MessengerInterface::TYPE_ERROR => [],
+    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 @@
+<?php
+
+namespace Drupal\Core\Messenger;
+
+use Drupal\Core\PageCache\ResponsePolicy\KillSwitch;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
+
+/**
+ * The messenger service.
+ *
+ * @internal
+ */
+class Messenger extends BaseMessenger {
+
+  /**
+   * The flash bag.
+   *
+   * @var \Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface
+   */
+  protected $flashBag;
+
+  /**
+   * The kill switch.
+   *
+   * @var \Drupal\Core\PageCache\ResponsePolicy\KillSwitch
+   */
+  protected $killSwitch;
+
+  /**
+   * Messenger constructor.
+   *
+   * @param \Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface $flash_bag
+   *   The flash bag.
+   * @param \Drupal\Core\PageCache\ResponsePolicy\KillSwitch $killSwitch
+   *   The kill switch.
+   */
+  public function __construct(FlashBagInterface $flash_bag, KillSwitch $killSwitch) {
+    $this->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 <em>markup!</em> (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/KernelTests/Core/Messenger/ChainedMessengerTest.php b/core/tests/Drupal/KernelTests/Core/Messenger/ChainedMessengerTest.php
new file mode 100644
index 0000000000..eae55a6549
--- /dev/null
+++ b/core/tests/Drupal/KernelTests/Core/Messenger/ChainedMessengerTest.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Drupal\KernelTests\Core\Messenger;
+
+use Drupal\Core\Messenger\ChainedMessenger;
+use Drupal\Core\Messenger\MemoryMessenger;
+use Drupal\Core\Messenger\Messenger;
+use Drupal\Core\Messenger\MessengerInterface;
+use Drupal\KernelTests\KernelTestBase;
+
+/**
+ * @group Messenger
+ * @coversDefaultClass \Drupal\Core\Messenger\ChainedMessenger
+ */
+class ChainedMessengerTest extends KernelTestBase {
+
+  /**
+   * @covers ::all
+   * @covers ::addMessage
+   * @covers ::addError
+   * @covers ::addStatus
+   * @covers ::addWarning
+   * @covers ::getMessenger
+   * @covers ::mergeMessages
+   */
+  public function testMessages() {
+    // Save the current container for later use.
+    $container = \Drupal::getContainer();
+
+    // Unset the container to mimic not having one.
+    \Drupal::unsetContainer();
+
+    // Create a new ChainedMessenger instance.
+    $messenger = new ChainedMessenger();
+
+    // Verify that the messenger returned is an instance of MemoryMessenger.
+    $this->assertInstanceOf(MemoryMessenger::class, $messenger->getMessenger());
+
+    // Add messages.
+    $messenger->addMessage('Foobar');
+    $messenger->addError('Foo');
+    $messenger->addStatus('Bar');
+    $messenger->addWarning('Fiz');
+
+    // Restore the container.
+    \Drupal::setContainer($container);
+
+    // Verify that the messenger returned is the Messenger service.
+    $this->assertInstanceOf(Messenger::class, $messenger->getMessenger());
+
+    // Add more messages.
+    $messenger->addMessage('Platypus');
+    $messenger->addError('Rhinoceros');
+    $messenger->addStatus('Giraffe');
+    $messenger->addWarning('Cheetah');
+
+    // Verify that all the messages are present and accounted for.
+    $messages = $messenger->all();
+    $this->assertContains('Foobar', $messages[MessengerInterface::TYPE_STATUS]);
+    $this->assertContains('Foo', $messages[MessengerInterface::TYPE_ERROR]);
+    $this->assertContains('Bar', $messages[MessengerInterface::TYPE_STATUS]);
+    $this->assertContains('Fiz', $messages[MessengerInterface::TYPE_WARNING]);
+    $this->assertContains('Platypus', $messages[MessengerInterface::TYPE_STATUS]);
+    $this->assertContains('Rhinoceros', $messages[MessengerInterface::TYPE_ERROR]);
+    $this->assertContains('Giraffe', $messages[MessengerInterface::TYPE_STATUS]);
+    $this->assertContains('Cheetah', $messages[MessengerInterface::TYPE_WARNING]);
+  }
+
+}
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',
     ];
   }
 
-- 
2.14.1

