From 492b7a181da3d074beb2e62f7e69ffc27f79d7f6 Mon Sep 17 00:00:00 2001
From: catch <catch@35733.no-reply.drupal.org>
Date: Wed, 18 Aug 2021 09:53:24 +0100
Subject: [PATCH] Issue #2473875 by znerol, alexpott, andypost, kim.pepper,
 amit.drupal, raman.b, martin107, joachim: Convert uses of $_SESSION to
 symfony session retrieved from the request

---
 .../scaffold/files/default.services.yml       |  9 ++--
 .../PageCache/RequestPolicy/NoSessionOpen.php |  6 +--
 .../big_pipe_test/big_pipe_test.module        |  5 +-
 .../dblog/src/Controller/DbLogController.php  | 18 +++++--
 .../src/Form/DblogClearLogConfirmForm.php     |  2 +-
 .../dblog/src/Form/DblogFilterForm.php        | 14 ++++--
 core/modules/file/file.module                 |  4 +-
 .../locale/src/Form/TranslateFilterForm.php   |  8 +--
 .../locale/src/Form/TranslateFormBase.php     | 12 +++--
 .../src/Controller/MigrateController.php      |  9 ++--
 .../src/Controller/DbUpdateController.php     | 36 ++++++++------
 core/modules/system/system.install            |  2 +-
 .../src/Form/FormTestStorageForm.php          | 10 ++--
 .../modules/menu_test/menu_test.services.yml  |  1 +
 .../menu_test/src/Access/AccessCheck.php      | 40 ++++++++++++---
 .../modules/menu_test/src/TestControllers.php | 17 ++++---
 .../session_exists_cache_context_test.module  |  5 +-
 core/modules/user/src/AccountForm.php         | 10 ++--
 .../user/src/Controller/UserController.php    |  6 ++-
 .../UserAccountFormPasswordResetTest.php      | 12 ++++-
 .../exposed_form/ExposedFormPluginBase.php    |  7 ++-
 .../Plugin/views/filter/FilterPluginBase.php  | 49 ++++++++++---------
 sites/default/default.services.yml            |  9 ++--
 23 files changed, 184 insertions(+), 107 deletions(-)

diff --git a/core/assets/scaffold/files/default.services.yml b/core/assets/scaffold/files/default.services.yml
index d57cf0e0bfeb..d21a5c78d42c 100644
--- a/core/assets/scaffold/files/default.services.yml
+++ b/core/assets/scaffold/files/default.services.yml
@@ -11,10 +11,11 @@ parameters:
     # @default 100
     gc_divisor: 100
     #
-    # Set session lifetime (in seconds), i.e. the time from the user's last
-    # visit to the active session may be deleted by the session garbage
-    # collector. When a session is deleted, authenticated users are logged out,
-    # and the contents of the user's $_SESSION variable is discarded.
+    # Set session lifetime (in seconds), i.e. the grace period for session
+    # data. Sessions are deleted by the session garbage collector after one
+    # session lifetime has elapsed since the user's last visit. When a session
+    # is deleted, authenticated users are logged out, and the contents of the
+    # user's session is discarded.
     # @default 200000
     gc_maxlifetime: 200000
     #
diff --git a/core/lib/Drupal/Core/PageCache/RequestPolicy/NoSessionOpen.php b/core/lib/Drupal/Core/PageCache/RequestPolicy/NoSessionOpen.php
index e29ae0bd032b..38e51dfc9054 100644
--- a/core/lib/Drupal/Core/PageCache/RequestPolicy/NoSessionOpen.php
+++ b/core/lib/Drupal/Core/PageCache/RequestPolicy/NoSessionOpen.php
@@ -10,9 +10,9 @@
  * A policy allowing delivery of cached pages when there is no session open.
  *
  * Do not serve cached pages to authenticated users, or to anonymous users when
- * $_SESSION is non-empty. $_SESSION may contain status messages from a form
- * submission, the contents of a shopping cart, or other user-specific content
- * that should not be cached and displayed to other users.
+ * the user's session is non-empty. The user's session may contain status
+ * messages from a form submission, the contents of a shopping cart, or other
+ * user-specific content that should not be cached and displayed to other users.
  */
 class NoSessionOpen implements RequestPolicyInterface {
 
diff --git a/core/modules/big_pipe/tests/modules/big_pipe_test/big_pipe_test.module b/core/modules/big_pipe/tests/modules/big_pipe_test/big_pipe_test.module
index 67c805bb6c55..739825e17aef 100644
--- a/core/modules/big_pipe/tests/modules/big_pipe_test/big_pipe_test.module
+++ b/core/modules/big_pipe/tests/modules/big_pipe_test/big_pipe_test.module
@@ -12,7 +12,8 @@ function big_pipe_test_page_top(array &$page_top) {
   // Ensure this hook is invoked on every page load.
   $page_top['#cache']['max-age'] = 0;
 
-  if (\Drupal::request()->query->get('trigger_session')) {
-    $_SESSION['big_pipe_test'] = TRUE;
+  $request = \Drupal::request();
+  if ($request->query->get('trigger_session')) {
+    $request->getSession()->set('big_pipe_test', TRUE);
   }
 }
diff --git a/core/modules/dblog/src/Controller/DbLogController.php b/core/modules/dblog/src/Controller/DbLogController.php
index 03d2ccc49308..7a21882e6d74 100644
--- a/core/modules/dblog/src/Controller/DbLogController.php
+++ b/core/modules/dblog/src/Controller/DbLogController.php
@@ -18,6 +18,7 @@
 use Drupal\Core\Url;
 use Drupal\user\Entity\User;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
 use Drupal\Core\Link;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
@@ -118,6 +119,9 @@ public static function getLogLevelClassMap() {
    * Messages are truncated at 56 chars.
    * Full-length messages can be viewed on the message details page.
    *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request.
+   *
    * @return array
    *   A render array as expected by
    *   \Drupal\Core\Render\RendererInterface::render().
@@ -125,9 +129,9 @@ public static function getLogLevelClassMap() {
    * @see Drupal\dblog\Form\DblogClearLogConfirmForm
    * @see Drupal\dblog\Controller\DbLogController::eventDetails()
    */
-  public function overview() {
+  public function overview(Request $request) {
 
-    $filter = $this->buildFilterQuery();
+    $filter = $this->buildFilterQuery($request);
     $rows = [];
 
     $classes = static::getLogLevelClassMap();
@@ -316,12 +320,16 @@ public function eventDetails($event_id) {
   /**
    * Builds a query for database log administration filters based on session.
    *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request.
+   *
    * @return array|null
    *   An associative array with keys 'where' and 'args' or NULL if there were
    *   no filters set.
    */
-  protected function buildFilterQuery() {
-    if (empty($_SESSION['dblog_overview_filter'])) {
+  protected function buildFilterQuery(Request $request) {
+    $session_filters = $request->getSession()->get('dblog_overview_filter', []);
+    if (empty($session_filters)) {
       return;
     }
 
@@ -331,7 +339,7 @@ protected function buildFilterQuery() {
 
     // Build query.
     $where = $args = [];
-    foreach ($_SESSION['dblog_overview_filter'] as $key => $filter) {
+    foreach ($session_filters as $key => $filter) {
       $filter_where = [];
       foreach ($filter as $value) {
         $filter_where[] = $filters[$key]['where'];
diff --git a/core/modules/dblog/src/Form/DblogClearLogConfirmForm.php b/core/modules/dblog/src/Form/DblogClearLogConfirmForm.php
index e567729b36a1..a77aa5caadd7 100644
--- a/core/modules/dblog/src/Form/DblogClearLogConfirmForm.php
+++ b/core/modules/dblog/src/Form/DblogClearLogConfirmForm.php
@@ -66,7 +66,7 @@ public function getCancelUrl() {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
-    $_SESSION['dblog_overview_filter'] = [];
+    $this->getRequest()->getSession()->remove('dblog_overview_filter');
     $this->connection->truncate('watchdog')->execute();
     $this->messenger()->addStatus($this->t('Database log cleared.'));
     $form_state->setRedirectUrl($this->getCancelUrl());
diff --git a/core/modules/dblog/src/Form/DblogFilterForm.php b/core/modules/dblog/src/Form/DblogFilterForm.php
index 21619d6fc2b4..08b7f24e4127 100644
--- a/core/modules/dblog/src/Form/DblogFilterForm.php
+++ b/core/modules/dblog/src/Form/DblogFilterForm.php
@@ -30,6 +30,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#title' => $this->t('Filter log messages'),
       '#open' => TRUE,
     ];
+    $session_filters = $this->getRequest()->getSession()->get('dblog_overview_filter', []);
     foreach ($filters as $key => $filter) {
       $form['filters']['status'][$key] = [
         '#title' => $filter['title'],
@@ -38,8 +39,9 @@ public function buildForm(array $form, FormStateInterface $form_state) {
         '#size' => 8,
         '#options' => $filter['options'],
       ];
-      if (!empty($_SESSION['dblog_overview_filter'][$key])) {
-        $form['filters']['status'][$key]['#default_value'] = $_SESSION['dblog_overview_filter'][$key];
+
+      if (!empty($session_filters[$key])) {
+        $form['filters']['status'][$key]['#default_value'] = $session_filters[$key];
       }
     }
 
@@ -51,7 +53,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#type' => 'submit',
       '#value' => $this->t('Filter'),
     ];
-    if (!empty($_SESSION['dblog_overview_filter'])) {
+    if (!empty($session_filters)) {
       $form['filters']['actions']['reset'] = [
         '#type' => 'submit',
         '#value' => $this->t('Reset'),
@@ -76,11 +78,13 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
     $filters = dblog_filters();
+    $session_filters = $this->getRequest()->getSession()->get('dblog_overview_filter', []);
     foreach ($filters as $name => $filter) {
       if ($form_state->hasValue($name)) {
-        $_SESSION['dblog_overview_filter'][$name] = $form_state->getValue($name);
+        $session_filters[$name] = $form_state->getValue($name);
       }
     }
+    $this->getRequest()->getSession()->set('dblog_overview_filter', $session_filters);
   }
 
   /**
@@ -92,7 +96,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
    *   The current state of the form.
    */
   public function resetForm(array &$form, FormStateInterface $form_state) {
-    $_SESSION['dblog_overview_filter'] = [];
+    $this->getRequest()->getSession()->remove('dblog_overview_filter');
   }
 
 }
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index 64a90a6b3ea0..502fd922d906 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -774,7 +774,7 @@ function file_cron() {
  */
 function _file_save_upload_from_form(array $element, FormStateInterface $form_state, $delta = NULL, $replace = FileSystemInterface::EXISTS_RENAME) {
   // Get all errors set before calling this method. This will also clear them
-  // from $_SESSION.
+  // from the messenger service.
   $errors_before = \Drupal::messenger()->deleteByType(MessengerInterface::TYPE_ERROR);
 
   $upload_location = isset($element['#upload_location']) ? $element['#upload_location'] : FALSE;
@@ -784,7 +784,7 @@ function _file_save_upload_from_form(array $element, FormStateInterface $form_st
   $result = file_save_upload($upload_name, $upload_validators, $upload_location, $delta, $replace);
 
   // Get new errors that are generated while trying to save the upload. This
-  // will also clear them from $_SESSION.
+  // will also clear them from the messenger service.
   $errors_new = \Drupal::messenger()->deleteByType(MessengerInterface::TYPE_ERROR);
   if (!empty($errors_new)) {
 
diff --git a/core/modules/locale/src/Form/TranslateFilterForm.php b/core/modules/locale/src/Form/TranslateFilterForm.php
index f7a3d20ec113..2186187ad61a 100644
--- a/core/modules/locale/src/Form/TranslateFilterForm.php
+++ b/core/modules/locale/src/Form/TranslateFilterForm.php
@@ -67,7 +67,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#type' => 'submit',
       '#value' => $this->t('Filter'),
     ];
-    if (!empty($_SESSION['locale_translate_filter'])) {
+    if ($this->getRequest()->getSession()->has('locale_translate_filter')) {
       $form['filters']['actions']['reset'] = [
         '#type' => 'submit',
         '#value' => $this->t('Reset'),
@@ -83,11 +83,13 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
     $filters = $this->translateFilters();
+    $session_filters = $this->getRequest()->getSession()->get('locale_translate_filter', []);
     foreach ($filters as $name => $filter) {
       if ($form_state->hasValue($name)) {
-        $_SESSION['locale_translate_filter'][$name] = trim($form_state->getValue($name));
+        $session_filters[$name] = trim($form_state->getValue($name));
       }
     }
+    $this->getRequest()->getSession()->set('locale_translate_filter', $session_filters);
     $form_state->setRedirect('locale.translate_page');
   }
 
@@ -95,7 +97,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
    * Provides a submit handler for the reset button.
    */
   public function resetForm(array &$form, FormStateInterface $form_state) {
-    $_SESSION['locale_translate_filter'] = [];
+    $this->getRequest()->getSession()->remove('locale_translate_filter');
     $form_state->setRedirect('locale.translate_page');
   }
 
diff --git a/core/modules/locale/src/Form/TranslateFormBase.php b/core/modules/locale/src/Form/TranslateFormBase.php
index d83714e5423d..9d9df77e4050 100644
--- a/core/modules/locale/src/Form/TranslateFormBase.php
+++ b/core/modules/locale/src/Form/TranslateFormBase.php
@@ -125,22 +125,24 @@ protected function translateFilterValues($reset = FALSE) {
 
     $filter_values = [];
     $filters = $this->translateFilters();
+    $request = $this->getRequest();
+    $session_filters = $request->getSession()->get('locale_translate_filter', []);
     foreach ($filters as $key => $filter) {
       $filter_values[$key] = $filter['default'];
       // Let the filter defaults be overwritten by parameters in the URL.
-      if ($this->getRequest()->query->has($key)) {
+      if ($request->query->has($key)) {
         // Only allow this value if it was among the options, or
         // if there were no fixed options to filter for.
-        $value = $this->getRequest()->query->get($key);
+        $value = $request->query->get($key);
         if (!isset($filter['options']) || isset($filter['options'][$value])) {
           $filter_values[$key] = $value;
         }
       }
-      elseif (isset($_SESSION['locale_translate_filter'][$key])) {
+      elseif (isset($session_filters[$key])) {
         // Only allow this value if it was among the options, or
         // if there were no fixed options to filter for.
-        if (!isset($filter['options']) || isset($filter['options'][$_SESSION['locale_translate_filter'][$key]])) {
-          $filter_values[$key] = $_SESSION['locale_translate_filter'][$key];
+        if (!isset($filter['options']) || isset($filter['options'][$session_filters[$key]])) {
+          $filter_values[$key] = $session_filters[$key];
         }
       }
     }
diff --git a/core/modules/migrate_drupal_ui/src/Controller/MigrateController.php b/core/modules/migrate_drupal_ui/src/Controller/MigrateController.php
index 0947004adbd1..148838292a10 100644
--- a/core/modules/migrate_drupal_ui/src/Controller/MigrateController.php
+++ b/core/modules/migrate_drupal_ui/src/Controller/MigrateController.php
@@ -3,6 +3,7 @@
 namespace Drupal\migrate_drupal_ui\Controller;
 
 use Drupal\Core\Controller\ControllerBase;
+use Symfony\Component\HttpFoundation\Request;
 
 /**
  * Provides controller methods for the migration.
@@ -12,14 +13,16 @@ class MigrateController extends ControllerBase {
   /**
    * Sets a log filter and redirects to the log.
    *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request.
+   *
    * @return \Symfony\Component\HttpFoundation\RedirectResponse
    *   A redirect response object that may be returned by the controller.
    */
-  public function showLog() {
+  public function showLog(Request $request) {
     // Sets both the session and the query parameter so that it works correctly
     // with both the watchdog view and the fallback.
-    $_SESSION['dblog_overview_filter'] = [];
-    $_SESSION['dblog_overview_filter']['type'] = ['migrate_drupal_ui' => 'migrate_drupal_ui'];
+    $request->getSession()->set('dblog_overview_filter', ['type' => ['migrate_drupal_ui' => 'migrate_drupal_ui']]);
     return $this->redirect('dblog.overview', [], ['query' => ['type' => ['migrate_drupal_ui']]]);
   }
 
diff --git a/core/modules/system/src/Controller/DbUpdateController.php b/core/modules/system/src/Controller/DbUpdateController.php
index 3136eaddb31f..be3adb210fad 100644
--- a/core/modules/system/src/Controller/DbUpdateController.php
+++ b/core/modules/system/src/Controller/DbUpdateController.php
@@ -147,13 +147,13 @@ public function handle($op, Request $request) {
     drupal_load_updates();
 
     if ($request->query->get('continue')) {
-      $_SESSION['update_ignore_warnings'] = TRUE;
+      $request->getSession()->set('update_ignore_warnings', TRUE);
     }
 
     $regions = [];
     $requirements = update_check_requirements();
     $severity = drupal_requirements_severity($requirements);
-    if ($severity == REQUIREMENT_ERROR || ($severity == REQUIREMENT_WARNING && empty($_SESSION['update_ignore_warnings']))) {
+    if ($severity == REQUIREMENT_ERROR || ($severity == REQUIREMENT_WARNING && !$request->getSession()->has('update_ignore_warnings'))) {
       $regions['sidebar_first'] = $this->updateTasksList('requirements');
       $output = $this->requirements($severity, $requirements, $request);
     }
@@ -397,6 +397,12 @@ protected function results(Request $request) {
     // @todo Simplify with https://www.drupal.org/node/2548095
     $base_url = str_replace('/update.php', '', $request->getBaseUrl());
 
+    // Retrieve and remove session information.
+    $session = $request->getSession();
+    $update_results = $session->remove('update_results');
+    $update_success = $session->remove('update_success');
+    $session->remove('update_ignore_warnings');
+
     // Report end result.
     $dblog_exists = $this->moduleHandler->moduleExists('dblog');
     if ($dblog_exists && $this->account->hasPermission('access site reports')) {
@@ -408,12 +414,13 @@ protected function results(Request $request) {
       $log_message = $this->t('All errors have been logged.');
     }
 
-    if (!empty($_SESSION['update_success'])) {
+    if ($update_success) {
       $message = '<p>' . $this->t('Updates were attempted. If you see no failures below, you may proceed happily back to your <a href=":url">site</a>. Otherwise, you may need to update your database manually.', [':url' => Url::fromRoute('<front>')->setOption('base_url', $base_url)->toString(TRUE)->getGeneratedUrl()]) . ' ' . $log_message . '</p>';
     }
     else {
-      $last = reset($_SESSION['updates_remaining']);
-      list($module, $version) = array_pop($last);
+      $last = $session->get('updates_remaining');
+      $last = reset($last);
+      [$module, $version] = array_pop($last);
       $message = '<p class="error">' . $this->t('The update process was aborted prematurely while running <strong>update #@version in @module.module</strong>.', [
         '@version' => $version,
         '@module' => $module,
@@ -437,9 +444,9 @@ protected function results(Request $request) {
     ];
 
     // Output a list of info messages.
-    if (!empty($_SESSION['update_results'])) {
+    if (!empty($update_results)) {
       $all_messages = [];
-      foreach ($_SESSION['update_results'] as $module => $updates) {
+      foreach ($update_results as $module => $updates) {
         if ($module != '#abort') {
           $module_has_message = FALSE;
           $info_messages = [];
@@ -501,9 +508,6 @@ protected function results(Request $request) {
         ];
       }
     }
-    unset($_SESSION['update_results']);
-    unset($_SESSION['update_success']);
-    unset($_SESSION['update_ignore_warnings']);
 
     return $build;
   }
@@ -576,7 +580,7 @@ protected function triggerBatch(Request $request) {
     $maintenance_mode = $this->state->get('system.maintenance_mode', FALSE);
     // Store the current maintenance mode status in the session so that it can
     // be restored at the end of the batch.
-    $_SESSION['maintenance_mode'] = $maintenance_mode;
+    $request->getSession()->set('maintenance_mode', $maintenance_mode);
     // During the update, always put the site into maintenance mode so that
     // in-progress schema changes do not affect visiting users.
     if (empty($maintenance_mode)) {
@@ -654,16 +658,16 @@ public static function batchFinished($success, $results, $operations) {
     // No updates to run, so caches won't get flushed later.  Clear them now.
     drupal_flush_all_caches();
 
-    $_SESSION['update_results'] = $results;
-    $_SESSION['update_success'] = $success;
-    $_SESSION['updates_remaining'] = $operations;
+    $session = \Drupal::request()->getSession();
+    $session->set('update_results', $results);
+    $session->set('update_success', $success);
+    $session->set('updates_remaining', $operations);
 
     // Now that the update is done, we can put the site back online if it was
     // previously not in maintenance mode.
-    if (empty($_SESSION['maintenance_mode'])) {
+    if (!$session->remove('maintenance_mode')) {
       \Drupal::state()->set('system.maintenance_mode', FALSE);
     }
-    unset($_SESSION['maintenance_mode']);
   }
 
   /**
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index f0a4f317be3f..0e74eda76d01 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -1372,7 +1372,7 @@ function system_schema() {
         'default' => 0,
       ],
       'session' => [
-        'description' => 'The serialized contents of $_SESSION, an array of name/value pairs that persists across page requests by this session ID. Drupal loads $_SESSION from here at the start of each request and saves it at the end.',
+        'description' => 'The serialized contents of the user\'s session, an array of name/value pairs that persists across page requests by this session ID. Drupal loads the user\'s session from here at the start of each request and saves it at the end.',
         'type' => 'blob',
         'not null' => FALSE,
         'size' => 'big',
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestStorageForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestStorageForm.php
index 5cc09b728069..785501390cf5 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestStorageForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestStorageForm.php
@@ -34,10 +34,11 @@ public function buildForm(array $form, FormStateInterface $form_state) {
     }
     // Initialize
     $storage = $form_state->getStorage();
+    $session = $this->getRequest()->getSession();
     if (empty($storage)) {
       $user_input = $form_state->getUserInput();
       if (empty($user_input)) {
-        $_SESSION['constructions'] = 0;
+        $session->set('constructions', 0);
       }
       // Put the initial thing into the storage
       $storage = [
@@ -49,8 +50,9 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       $form_state->setStorage($storage);
     }
     // Count how often the form is constructed.
-    $_SESSION['constructions']++;
-    $this->messenger()->addStatus("Form constructions: " . $_SESSION['constructions']);
+    $counter = $session->get('constructions');
+    $session->set('constructions', ++$counter);
+    $this->messenger()->addStatus("Form constructions: " . $counter);
 
     $form['title'] = [
       '#type' => 'textfield',
@@ -137,7 +139,7 @@ public function continueSubmitForm(array &$form, FormStateInterface $form_state)
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
     $this->messenger()->addStatus("Title: " . Html::escape($form_state->getValue('title')));
-    $this->messenger()->addStatus("Form constructions: " . $_SESSION['constructions']);
+    $this->messenger()->addStatus("Form constructions: " . $this->getRequest()->getSession()->get('constructions'));
     if ($form_state->has(['thing', 'changed'])) {
       $this->messenger()->addStatus("The thing has been changed.");
     }
diff --git a/core/modules/system/tests/modules/menu_test/menu_test.services.yml b/core/modules/system/tests/modules/menu_test/menu_test.services.yml
index 81f1f50b9578..e1a0aa059bce 100644
--- a/core/modules/system/tests/modules/menu_test/menu_test.services.yml
+++ b/core/modules/system/tests/modules/menu_test/menu_test.services.yml
@@ -6,5 +6,6 @@ services:
 
   access_check.menu_test_session:
     class: Drupal\menu_test\Access\AccessCheck
+    arguments: ['@request_stack']
     tags:
       - { name: access_check, applies_to: _menu_test_session_access }
diff --git a/core/modules/system/tests/modules/menu_test/src/Access/AccessCheck.php b/core/modules/system/tests/modules/menu_test/src/Access/AccessCheck.php
index e87fcfa02257..cd60aac30dba 100644
--- a/core/modules/system/tests/modules/menu_test/src/Access/AccessCheck.php
+++ b/core/modules/system/tests/modules/menu_test/src/Access/AccessCheck.php
@@ -3,12 +3,41 @@
 namespace Drupal\menu_test\Access;
 
 use Drupal\Core\Access\AccessResult;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\Routing\Access\AccessInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\RequestStack;
 
 /**
  * Checks access based on the 'menu_test' key in session.
  */
-class AccessCheck implements AccessInterface {
+class AccessCheck implements AccessInterface, ContainerInjectionInterface {
+
+  /**
+   * The request stack.
+   *
+   * @var \Symfony\Component\HttpFoundation\RequestStack
+   */
+  protected $requestStack;
+
+  /**
+   * Constructs a new AccessCheck class.
+   *
+   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
+   *   The request stack.
+   */
+  public function __construct(RequestStack $request_stack) {
+    $this->requestStack = $request_stack;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('request_stack')
+    );
+  }
 
   /**
    * Check to see if user accessed this page.
@@ -17,12 +46,9 @@ class AccessCheck implements AccessInterface {
    *   The access result.
    */
   public function access() {
-    if (!isset($_SESSION['menu_test'])) {
-      $result = AccessResult::allowed();
-    }
-    else {
-      $result = AccessResult::allowedIf($_SESSION['menu_test'] < 2);
-    }
+    $result = AccessResult::allowedIf(
+      $this->requestStack->getCurrentRequest()->getSession()->get('menu_test', 0) < 2
+    );
     return $result->setCacheMaxAge(0);
   }
 
diff --git a/core/modules/system/tests/modules/menu_test/src/TestControllers.php b/core/modules/system/tests/modules/menu_test/src/TestControllers.php
index dafaab0e511c..0e94c483ac51 100644
--- a/core/modules/system/tests/modules/menu_test/src/TestControllers.php
+++ b/core/modules/system/tests/modules/menu_test/src/TestControllers.php
@@ -3,6 +3,7 @@
 namespace Drupal\menu_test;
 
 use Drupal\Component\Render\FormattableMarkup;
+use Symfony\Component\HttpFoundation\Request;
 
 /**
  * Controllers for testing the menu integration routing system.
@@ -32,13 +33,17 @@ public function test2() {
 
   /**
    * Prints out test data.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request.
+   *
+   * @return array
+   *   Render array.
    */
-  public function testSession() {
-    if (!isset($_SESSION['menu_test'])) {
-      $_SESSION['menu_test'] = 0;
-    }
-    $_SESSION['menu_test']++;
-    return ['#markup' => new FormattableMarkup('Session menu_test is @count', ['@count' => $_SESSION['menu_test']])];
+  public function testSession(Request $request) {
+    $counter = $request->getSession()->get('menu_test', 0);
+    $request->getSession()->set('menu_test', ++$counter);
+    return ['#markup' => new FormattableMarkup('Session menu_test is @count', ['@count' => $counter])];
   }
 
   /**
diff --git a/core/modules/system/tests/modules/session_exists_cache_context_test/session_exists_cache_context_test.module b/core/modules/system/tests/modules/session_exists_cache_context_test/session_exists_cache_context_test.module
index 60d2452836c6..2be874da7a66 100644
--- a/core/modules/system/tests/modules/session_exists_cache_context_test/session_exists_cache_context_test.module
+++ b/core/modules/system/tests/modules/session_exists_cache_context_test/session_exists_cache_context_test.module
@@ -23,7 +23,8 @@ function session_exists_cache_context_test_page_top(array &$page_top) {
     ],
   ];
 
-  if (\Drupal::request()->query->get('trigger_session')) {
-    $_SESSION['session_exists_cache_context_test'] = TRUE;
+  $request = \Drupal::request();
+  if ($request->query->get('trigger_session')) {
+    $request->getSession()->set('session_exists_cache_context_test', TRUE);
   }
 }
diff --git a/core/modules/user/src/AccountForm.php b/core/modules/user/src/AccountForm.php
index 2358d9b654de..e96c4dfa1665 100644
--- a/core/modules/user/src/AccountForm.php
+++ b/core/modules/user/src/AccountForm.php
@@ -130,9 +130,11 @@ public function form(array $form, FormStateInterface $form_state) {
       // To skip the current password field, the user must have logged in via a
       // one-time link and have the token in the URL. Store this in $form_state
       // so it persists even on subsequent Ajax requests.
-      if (!$form_state->get('user_pass_reset') && ($token = $this->getRequest()->query->get('pass-reset-token'))) {
+      $request = $this->getRequest();
+      if (!$form_state->get('user_pass_reset') && ($token = $request->query->get('pass-reset-token'))) {
         $session_key = 'pass_reset_' . $account->id();
-        $user_pass_reset = isset($_SESSION[$session_key]) && hash_equals($_SESSION[$session_key], $token);
+        $session_value = $request->getSession()->get($session_key);
+        $user_pass_reset = isset($session_value) && hash_equals($session_value, $token);
         $form_state->set('user_pass_reset', $user_pass_reset);
       }
 
@@ -431,9 +433,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     $user = $this->getEntity($form_state);
     // If there's a session set to the users id, remove the password reset tag
     // since a new password was saved.
-    if (isset($_SESSION['pass_reset_' . $user->id()])) {
-      unset($_SESSION['pass_reset_' . $user->id()]);
-    }
+    $this->getRequest()->getSession()->remove('pass_reset_' . $user->id());
   }
 
 }
diff --git a/core/modules/user/src/Controller/UserController.php b/core/modules/user/src/Controller/UserController.php
index dd172448acc2..ac3540cb1a43 100644
--- a/core/modules/user/src/Controller/UserController.php
+++ b/core/modules/user/src/Controller/UserController.php
@@ -210,6 +210,8 @@ public function getResetPassForm(Request $request, $uid) {
    *   The current timestamp.
    * @param string $hash
    *   Login link hash.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request.
    *
    * @return \Symfony\Component\HttpFoundation\RedirectResponse
    *   Returns a redirect to the user edit form if the information is correct.
@@ -219,7 +221,7 @@ public function getResetPassForm(Request $request, $uid) {
    * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
    *   If $uid is for a blocked user or invalid user ID.
    */
-  public function resetPassLogin($uid, $timestamp, $hash) {
+  public function resetPassLogin($uid, $timestamp, $hash, Request $request) {
     // The current user is not logged in, so check the parameters.
     $current = REQUEST_TIME;
     /** @var \Drupal\user\UserInterface $user */
@@ -246,7 +248,7 @@ public function resetPassLogin($uid, $timestamp, $hash) {
       // Let the user's password be changed without the current password
       // check.
       $token = Crypt::randomBytesBase64(55);
-      $_SESSION['pass_reset_' . $user->id()] = $token;
+      $request->getSession()->set('pass_reset_' . $user->id(), $token);
       // Clear any flood events for this user.
       $this->flood->clear('user.password_request_user', $uid);
       return $this->redirect(
diff --git a/core/modules/user/tests/src/Kernel/UserAccountFormPasswordResetTest.php b/core/modules/user/tests/src/Kernel/UserAccountFormPasswordResetTest.php
index 6318e37108f2..703775bdd564 100644
--- a/core/modules/user/tests/src/Kernel/UserAccountFormPasswordResetTest.php
+++ b/core/modules/user/tests/src/Kernel/UserAccountFormPasswordResetTest.php
@@ -4,6 +4,7 @@
 
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\user\Entity\User;
+use Symfony\Component\HttpFoundation\Session\Session;
 
 /**
  * Verifies that the password reset behaves as expected with form elements.
@@ -50,11 +51,18 @@ protected function setUp(): void {
    * Tests the reset token used only from query string.
    */
   public function testPasswordResetToken() {
-    $token = 'VALID_TOKEN';
-    $_SESSION['pass_reset_1'] = $token;
     /** @var \Symfony\Component\HttpFoundation\Request $request */
     $request = $this->container->get('request_stack')->getCurrentRequest();
 
+    // @todo: Replace with $request->getSession() as soon as the session is
+    // present in KernelTestBase.
+    // see: https://www.drupal.org/node/2484991
+    $session = new Session();
+    $request->setSession($session);
+
+    $token = 'VALID_TOKEN';
+    $session->set('pass_reset_1', $token);
+
     // Set token in query string.
     $request->query->set('pass-reset-token', $token);
     $form = $this->buildAccountForm('default');
diff --git a/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php b/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php
index 75daffccb054..ff6a2dce2e6e 100644
--- a/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php
+++ b/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php
@@ -317,9 +317,12 @@ public function resetForm(&$form, FormStateInterface $form_state) {
     // remember settings.
     $display_id = ($this->view->display_handler->isDefaulted('filters')) ? 'default' : $this->view->current_display;
 
-    if (isset($_SESSION['views'][$this->view->storage->id()][$display_id])) {
-      unset($_SESSION['views'][$this->view->storage->id()][$display_id]);
+    $session = $this->view->getRequest()->getSession();
+    $views_session = $session->get('views', []);
+    if (isset($views_session[$this->view->storage->id()][$display_id])) {
+      unset($views_session[$this->view->storage->id()][$display_id]);
     }
+    $session->set('views', $views_session);
 
     // Set the form to allow redirect.
     if (empty($this->view->live_preview) && !\Drupal::request()->isXmlHttpRequest()) {
diff --git a/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php b/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
index 213c8ea2e67b..4d4cdb8be06f 100644
--- a/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
+++ b/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
@@ -1452,22 +1452,20 @@ public function storeGroupInput($input, $status) {
 
     // False means that we got a setting that means to recurse ourselves,
     // so we should erase whatever happened to be there.
-    if ($status === FALSE && isset($_SESSION['views'][$this->view->storage->id()][$display_id])) {
-      $session = &$_SESSION['views'][$this->view->storage->id()][$display_id];
-
-      if (isset($session[$this->options['group_info']['identifier']])) {
-        unset($session[$this->options['group_info']['identifier']]);
-      }
+    $session = $this->view->getRequest()->getSession();
+    $views_session = $session->get('views', []);
+    if ($status === FALSE && isset($views_session[$this->view->storage->id()][$display_id])) {
+      unset($views_session[$this->view->storage->id()][$display_id][$this->options['group_info']['identifier']]);
     }
 
     if ($status !== FALSE) {
-      if (!isset($_SESSION['views'][$this->view->storage->id()][$display_id])) {
-        $_SESSION['views'][$this->view->storage->id()][$display_id] = [];
+      if (!isset($views_session[$this->view->storage->id()][$display_id])) {
+        $views_session[$this->view->storage->id()][$display_id] = [];
       }
-
-      $session = &$_SESSION['views'][$this->view->storage->id()][$display_id];
-
-      $session[$this->options['group_info']['identifier']] = $input[$this->options['group_info']['identifier']];
+      $views_session[$this->view->storage->id()][$display_id][$this->options['group_info']['identifier']] = $input[$this->options['group_info']['identifier']];
+    }
+    if (!empty($views_session)) {
+      $session->set('views', $views_session);
     }
   }
 
@@ -1560,29 +1558,34 @@ public function storeExposedInput($input, $status) {
 
     // False means that we got a setting that means to recurse ourselves,
     // so we should erase whatever happened to be there.
-    if (!$status && isset($_SESSION['views'][$this->view->storage->id()][$display_id])) {
-      $session = &$_SESSION['views'][$this->view->storage->id()][$display_id];
-      if ($operator && isset($session[$this->options['expose']['operator_id']])) {
-        unset($session[$this->options['expose']['operator_id']]);
+    $session = $this->view->getRequest()->getSession();
+    $views_session = $session->get('views', []);
+    if (!$status && isset($views_session[$this->view->storage->id()][$display_id])) {
+      $session_ref = &$views_session[$this->view->storage->id()][$display_id];
+      if ($operator && isset($session_ref[$this->options['expose']['operator_id']])) {
+        unset($session_ref[$this->options['expose']['operator_id']]);
       }
 
-      if (isset($session[$this->options['expose']['identifier']])) {
-        unset($session[$this->options['expose']['identifier']]);
+      if (isset($session_ref[$this->options['expose']['identifier']])) {
+        unset($session_ref[$this->options['expose']['identifier']]);
       }
     }
 
     if ($status) {
-      if (!isset($_SESSION['views'][$this->view->storage->id()][$display_id])) {
-        $_SESSION['views'][$this->view->storage->id()][$display_id] = [];
+      if (!isset($views_session[$this->view->storage->id()][$display_id])) {
+        $views_session[$this->view->storage->id()][$display_id] = [];
       }
 
-      $session = &$_SESSION['views'][$this->view->storage->id()][$display_id];
+      $session_ref = &$views_session[$this->view->storage->id()][$display_id];
 
       if ($operator && isset($input[$this->options['expose']['operator_id']])) {
-        $session[$this->options['expose']['operator_id']] = $input[$this->options['expose']['operator_id']];
+        $session_ref[$this->options['expose']['operator_id']] = $input[$this->options['expose']['operator_id']];
       }
 
-      $session[$this->options['expose']['identifier']] = $input[$this->options['expose']['identifier']];
+      $session_ref[$this->options['expose']['identifier']] = $input[$this->options['expose']['identifier']];
+    }
+    if (!empty($views_session)) {
+      $session->set('views', $views_session);
     }
   }
 
diff --git a/sites/default/default.services.yml b/sites/default/default.services.yml
index d57cf0e0bfeb..d21a5c78d42c 100644
--- a/sites/default/default.services.yml
+++ b/sites/default/default.services.yml
@@ -11,10 +11,11 @@ parameters:
     # @default 100
     gc_divisor: 100
     #
-    # Set session lifetime (in seconds), i.e. the time from the user's last
-    # visit to the active session may be deleted by the session garbage
-    # collector. When a session is deleted, authenticated users are logged out,
-    # and the contents of the user's $_SESSION variable is discarded.
+    # Set session lifetime (in seconds), i.e. the grace period for session
+    # data. Sessions are deleted by the session garbage collector after one
+    # session lifetime has elapsed since the user's last visit. When a session
+    # is deleted, authenticated users are logged out, and the contents of the
+    # user's session is discarded.
     # @default 200000
     gc_maxlifetime: 200000
     #
-- 
GitLab