diff --git a/core/core.services.yml b/core/core.services.yml index 831eb541ad..b1e587a3a3 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -1721,6 +1721,6 @@ services: - { name: backend_overridable } tempstore.shared: class: Drupal\Core\TempStore\SharedTempStoreFactory - arguments: ['@keyvalue.expirable', '@lock', '@request_stack', '%tempstore.expire%'] + arguments: ['@keyvalue.expirable', '@lock', '@request_stack', '@current_user', '@session', '%tempstore.expire%'] tags: - { name: backend_overridable } diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index c75da59c7a..d0031e3535 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -1547,6 +1547,8 @@ function install_bootstrap_full() { $session = \Drupal::service('session'); \Drupal::request()->setSession($session); $session->start(); + // Ensure the session is maintained throughout the install. + $session->set('_drupal_installing', TRUE); } /** @@ -1844,6 +1846,9 @@ function install_finished(&$install_state) { // Installation profiles are always loaded last. module_set_weight($profile, 1000); + // Clean up the session flag. + \Drupal::service('session')->remove('_drupal_installing'); + // Build the router once after installing all modules. // This would normally happen upon KernelEvents::TERMINATE, but since the // installer does not use an HttpKernel, that event is never triggered. diff --git a/core/lib/Drupal/Core/Session/MetadataBag.php b/core/lib/Drupal/Core/Session/MetadataBag.php index ee0fbd4855..dad5dabdf1 100644 --- a/core/lib/Drupal/Core/Session/MetadataBag.php +++ b/core/lib/Drupal/Core/Session/MetadataBag.php @@ -51,7 +51,8 @@ class MetadataBag extends SymfonyMetadataBag { /** * Clear the CSRF token seed. */ - public function clearCsrfTokenSeed() { + public function stampNew($lifetime = NULL) { + parent::stampNew($lifetime); unset($this->meta[static::CSRF_TOKEN_SEED]); } diff --git a/core/lib/Drupal/Core/Session/SessionManager.php b/core/lib/Drupal/Core/Session/SessionManager.php index 7981398678..0fcb915f25 100644 --- a/core/lib/Drupal/Core/Session/SessionManager.php +++ b/core/lib/Drupal/Core/Session/SessionManager.php @@ -2,7 +2,6 @@ namespace Drupal\Core\Session; -use Drupal\Component\Utility\Crypt; use Drupal\Core\Database\Connection; use Drupal\Core\DependencyInjection\DependencySerializationTrait; use Symfony\Component\HttpFoundation\RequestStack; @@ -119,17 +118,6 @@ class SessionManager extends NativeSessionStorage implements SessionManagerInter } if (empty($result)) { - // Randomly generate a session identifier for this request. This is - // necessary because \Drupal\Core\TempStore\SharedTempStoreFactory::get() - // wants to know the future session ID of a lazily started session in - // advance. - // - // @todo: With current versions of PHP there is little reason to generate - // the session id from within application code. Consider using the - // default php session id instead of generating a custom one: - // https://www.drupal.org/node/2238561 - $this->setId(Crypt::randomBytesBase64()); - // Initialize the session global and attach the Symfony session bags. $_SESSION = []; $this->loadSession(); @@ -210,32 +198,7 @@ class SessionManager extends NativeSessionStorage implements SessionManagerInter return; } - // We do not support the optional $destroy and $lifetime parameters as long - // as #2238561 remains open. - if ($destroy || isset($lifetime)) { - throw new \InvalidArgumentException('The optional parameters $destroy and $lifetime of SessionManager::regenerate() are not supported currently'); - } - - if ($this->isStarted()) { - $old_session_id = $this->getId(); - // Save and close the old session. Call the parent method to avoid issue - // with session destruction due to the session being considered obsolete. - parent::save(); - // Ensure the session is reloaded correctly. - $this->startedLazy = TRUE; - } - session_id(Crypt::randomBytesBase64()); - - $this->getMetadataBag()->clearCsrfTokenSeed(); - - if (isset($old_session_id)) { - $params = session_get_cookie_params(); - $expire = $params['lifetime'] ? REQUEST_TIME + $params['lifetime'] : 0; - setcookie($this->getName(), $this->getId(), $expire, $params['path'], $params['domain'], $params['secure'], $params['httponly']); - $this->migrateStoredSession($old_session_id); - } - - $this->startNow(); + return parent::regenerate($destroy, $lifetime); } /** @@ -326,18 +289,4 @@ class SessionManager extends NativeSessionStorage implements SessionManagerInter return array_intersect_key($mask, $_SESSION); } - /** - * Migrates the current session to a new session id. - * - * @param string $old_session_id - * The old session ID. The new session ID is $this->getId(). - */ - protected function migrateStoredSession($old_session_id) { - $fields = ['sid' => Crypt::hashBase64($this->getId())]; - $this->connection->update('sessions') - ->fields($fields) - ->condition('sid', Crypt::hashBase64($old_session_id)) - ->execute(); - } - } diff --git a/core/lib/Drupal/Core/TempStore/PrivateTempStore.php b/core/lib/Drupal/Core/TempStore/PrivateTempStore.php index 4c89ff4170..6696671534 100644 --- a/core/lib/Drupal/Core/TempStore/PrivateTempStore.php +++ b/core/lib/Drupal/Core/TempStore/PrivateTempStore.php @@ -2,6 +2,7 @@ namespace Drupal\Core\TempStore; +use Drupal\Component\Utility\Crypt; use Drupal\Core\DependencyInjection\DependencySerializationTrait; use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface; use Drupal\Core\Lock\LockBackendInterface; @@ -119,17 +120,16 @@ class PrivateTempStore { * Thrown when a lock for the backend storage could not be acquired. */ public function set($key, $value) { - // Ensure that an anonymous user has a session created for them, as - // otherwise subsequent page loads will not be able to retrieve their - // tempstore data. if ($this->currentUser->isAnonymous()) { - // @todo when https://www.drupal.org/node/2865991 is resolved, use force - // start session API rather than setting an arbitrary value directly. + // Ensure that an anonymous user has a session created for them, as + // otherwise subsequent page loads will not be able to retrieve their + // tempstore data. Note this has to be done before the key is created as + // the owner is used in key creation. $this->startSession(); - $this->requestStack - ->getCurrentRequest() - ->getSession() - ->set('core.tempstore.private', TRUE); + $session = $this->requestStack->getCurrentRequest()->getSession(); + if (!$session->has('core.tempstore.private.owner')) { + $session->set('core.tempstore.private.owner', Crypt::randomBytesBase64()); + } } $key = $this->createkey($key); @@ -224,8 +224,10 @@ class PrivateTempStore { protected function getOwner() { $owner = $this->currentUser->id(); if ($this->currentUser->isAnonymous()) { + // Check to see if an owner key exists in the session. $this->startSession(); - $owner = $this->requestStack->getCurrentRequest()->getSession()->getId(); + $session = $this->requestStack->getCurrentRequest()->getSession(); + $owner = $session->get('core.tempstore.private.owner'); } return $owner; } diff --git a/core/lib/Drupal/Core/TempStore/SharedTempStore.php b/core/lib/Drupal/Core/TempStore/SharedTempStore.php index 6bc0340752..70fdef5a1e 100644 --- a/core/lib/Drupal/Core/TempStore/SharedTempStore.php +++ b/core/lib/Drupal/Core/TempStore/SharedTempStore.php @@ -5,6 +5,7 @@ namespace Drupal\Core\TempStore; use Drupal\Core\DependencyInjection\DependencySerializationTrait; use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface; use Drupal\Core\Lock\LockBackendInterface; +use Drupal\Core\Session\AccountProxyInterface; use Symfony\Component\HttpFoundation\RequestStack; /** @@ -68,6 +69,13 @@ class SharedTempStore { */ protected $owner; + /** + * The current user. + * + * @var \Drupal\Core\Session\AccountProxyInterface + */ + protected $currentUser; + /** * The time to live for items in seconds. * @@ -90,14 +98,28 @@ class SharedTempStore { * The owner key to store along with the data (e.g. a user or session ID). * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack * The request stack. + * @param \Drupal\Core\Session\AccountProxyInterface $current_user + * The current user. * @param int $expire * The time to live for items, in seconds. */ - public function __construct(KeyValueStoreExpirableInterface $storage, LockBackendInterface $lock_backend, $owner, RequestStack $request_stack, $expire = 604800) { + public function __construct(KeyValueStoreExpirableInterface $storage, LockBackendInterface $lock_backend, $owner, RequestStack $request_stack, $current_user = NULL, $expire = 604800) { $this->storage = $storage; $this->lockBackend = $lock_backend; $this->owner = $owner; $this->requestStack = $request_stack; + if ($current_user instanceof AccountProxyInterface) { + $this->currentUser = $current_user; + } + else { + @trigger_error(__CLASS__ . '::__construct() now requires the current_user service to be injected. See https://www.drupal.org/node/3006268', E_USER_DEPRECATED); + $this->currentUser = \Drupal::currentUser(); + if (is_int($current_user)) { + // If the $current_user argument is numeric then this object has been + // instantiated with the old constructor signature. + $expire = $current_user; + } + } $this->expire = $expire; } @@ -150,7 +172,11 @@ class SharedTempStore { 'data' => $value, 'updated' => (int) $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME'), ]; - return $this->storage->setWithExpireIfNotExists($key, $value, $this->expire); + $set = $this->storage->setWithExpireIfNotExists($key, $value, $this->expire); + if ($set) { + $this->ensureAnonymousSession(); + } + return $set; } /** @@ -208,6 +234,7 @@ class SharedTempStore { 'data' => $value, 'updated' => (int) $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME'), ]; + $this->ensureAnonymousSession(); $this->storage->setWithExpire($key, $value, $this->expire); $this->lockBackend->release($key); } @@ -279,4 +306,15 @@ class SharedTempStore { return FALSE; } + /** + * Stores the owner in session if the user is anonymous. + * + * This method should be called when a value is set. + */ + protected function ensureAnonymousSession() { + if ($this->currentUser->isAnonymous()) { + $this->requestStack->getCurrentRequest()->getSession()->set('core.tempstore.shared.owner', $this->owner); + } + } + } diff --git a/core/lib/Drupal/Core/TempStore/SharedTempStoreFactory.php b/core/lib/Drupal/Core/TempStore/SharedTempStoreFactory.php index 90e816a7dd..9d0040205b 100644 --- a/core/lib/Drupal/Core/TempStore/SharedTempStoreFactory.php +++ b/core/lib/Drupal/Core/TempStore/SharedTempStoreFactory.php @@ -2,9 +2,12 @@ namespace Drupal\Core\TempStore; +use Drupal\Component\Utility\Crypt; use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface; use Drupal\Core\Lock\LockBackendInterface; +use Drupal\Core\Session\AccountProxyInterface; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Session\SessionInterface; /** * Creates a shared temporary storage for a collection. @@ -32,6 +35,20 @@ class SharedTempStoreFactory { */ protected $requestStack; + /** + * The current user. + * + * @var \Drupal\Core\Session\AccountProxyInterface + */ + protected $currentUser; + + /** + * The session service. + * + * @var \Symfony\Component\HttpFoundation\Session\SessionInterface + */ + protected $session; + /** * The time to live for items in seconds. * @@ -48,13 +65,38 @@ class SharedTempStoreFactory { * The lock object used for this data. * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack * The request stack. + * @param \Drupal\Core\Session\AccountProxyInterface $current_user + * The current user. + * @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session + * The session service. * @param int $expire * The time to live for items, in seconds. */ - public function __construct(KeyValueExpirableFactoryInterface $storage_factory, LockBackendInterface $lock_backend, RequestStack $request_stack, $expire = 604800) { + public function __construct(KeyValueExpirableFactoryInterface $storage_factory, LockBackendInterface $lock_backend, RequestStack $request_stack, $current_user = NULL, SessionInterface $session = NULL, $expire = 604800) { $this->storageFactory = $storage_factory; $this->lockBackend = $lock_backend; $this->requestStack = $request_stack; + + if ($current_user instanceof AccountProxyInterface) { + $this->currentUser = $current_user; + } + else { + $this->currentUser = \Drupal::currentUser(); + } + if ($session instanceof SessionInterface) { + $this->session = $session; + } + else { + $this->session = \Drupal::service('session'); + } + if (!($current_user instanceof AccountProxyInterface) || !($session instanceof SessionInterface)) { + @trigger_error(__CLASS__ . '::__construct() now requires the current_user and session services to be injected. See https://www.drupal.org/node/3006268', E_USER_DEPRECATED); + if (is_int($current_user)) { + // If the $current_user argument is numeric then this object has been + // instantiated with the old constructor signature. + $expire = $current_user; + } + } $this->expire = $expire; } @@ -76,12 +118,23 @@ class SharedTempStoreFactory { // Use the currently authenticated user ID or the active user ID unless // the owner is overridden. if (!isset($owner)) { - $owner = \Drupal::currentUser()->id() ?: session_id(); + $owner = $this->currentUser->id(); + if ($this->currentUser->isAnonymous()) { + $has_session = $this->requestStack + ->getCurrentRequest() + ->hasSession(); + if (!$has_session) { + $this->requestStack->getCurrentRequest()->setSession($this->session); + $this->session->start(); + } + // Anonymous users store a random identifier in session. + $owner = $this->requestStack->getCurrentRequest()->getSession()->get('core.tempstore.shared.owner', Crypt::randomBytesBase64()); + } } // Store the data for this collection in the database. $storage = $this->storageFactory->get("tempstore.shared.$collection"); - return new SharedTempStore($storage, $this->lockBackend, $owner, $this->requestStack, $this->expire); + return new SharedTempStore($storage, $this->lockBackend, $owner, $this->requestStack, $this->currentUser, $this->expire); } } diff --git a/core/modules/user/src/SharedTempStoreFactory.php b/core/modules/user/src/SharedTempStoreFactory.php index c955f07f7a..fe4deb00d3 100644 --- a/core/modules/user/src/SharedTempStoreFactory.php +++ b/core/modules/user/src/SharedTempStoreFactory.php @@ -35,12 +35,23 @@ class SharedTempStoreFactory extends CoreSharedTempStoreFactory { // Use the currently authenticated user ID or the active user ID unless // the owner is overridden. if (!isset($owner)) { - $owner = \Drupal::currentUser()->id() ?: session_id(); + $owner = $this->currentUser->id(); + if ($this->currentUser->isAnonymous()) { + $has_session = $this->requestStack + ->getCurrentRequest() + ->hasSession(); + if (!$has_session) { + $this->requestStack->getCurrentRequest()->setSession($this->session); + $this->session->start(); + } + // Anonymous users store a random identifier in session. + $owner = $this->requestStack->getCurrentRequest()->getSession()->get('core.tempstore.shared.owner', Crypt::randomBytesBase64()); + } } // Store the data for this collection in the database. $storage = $this->storageFactory->get("user.shared_tempstore.$collection"); - return new SharedTempStore($storage, $this->lockBackend, $owner, $this->requestStack, $this->expire); + return new SharedTempStore($storage, $this->lockBackend, $owner, $this->requestStack, $this->currentUser, $this->expire); } } diff --git a/core/modules/user/tests/src/Kernel/TempStoreDatabaseTest.php b/core/modules/user/tests/src/Kernel/TempStoreDatabaseTest.php index 51a3b9f8e7..ce2ea64764 100644 --- a/core/modules/user/tests/src/Kernel/TempStoreDatabaseTest.php +++ b/core/modules/user/tests/src/Kernel/TempStoreDatabaseTest.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\user\Kernel; use Drupal\Core\KeyValueStore\KeyValueExpirableFactory; +use Drupal\Core\Session\AccountProxyInterface; use Drupal\KernelTests\KernelTestBase; use Drupal\user\SharedTempStoreFactory; use Drupal\Core\Lock\DatabaseLockBackend; @@ -73,8 +74,17 @@ class TempStoreDatabaseTest extends KernelTestBase { * @expectedDeprecation \Drupal\user\SharedTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\SharedTempStore instead. See https://www.drupal.org/node/2935639. */ public function testUserTempStore() { + // Mock the current user service so that isAnonymous returns FALSE. + $current_user = $this->prophesize(AccountProxyInterface::class); + // Create a key/value collection. - $factory = new SharedTempStoreFactory(new KeyValueExpirableFactory(\Drupal::getContainer()), new DatabaseLockBackend(Database::getConnection()), $this->container->get('request_stack')); + $factory = new SharedTempStoreFactory( + new KeyValueExpirableFactory(\Drupal::getContainer()), + new DatabaseLockBackend(Database::getConnection()), + $this->container->get('request_stack'), + $current_user->reveal(), + $this->container->get('session') + ); $collection = $this->randomMachineName(); // Create two mock users. diff --git a/core/modules/user/tests/src/Unit/SharedTempStoreTest.php b/core/modules/user/tests/src/Unit/SharedTempStoreTest.php index 59a9d8f711..5339e02a64 100644 --- a/core/modules/user/tests/src/Unit/SharedTempStoreTest.php +++ b/core/modules/user/tests/src/Unit/SharedTempStoreTest.php @@ -2,11 +2,13 @@ namespace Drupal\Tests\user\Unit; +use Drupal\Core\Session\AccountProxyInterface; use Drupal\Tests\UnitTestCase; use Drupal\user\SharedTempStore; use Drupal\user\TempStoreException; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Session\SessionInterface; /** * @coversDefaultClass \Drupal\user\SharedTempStore @@ -76,9 +78,12 @@ class SharedTempStoreTest extends UnitTestCase { $this->lock = $this->createMock('Drupal\Core\Lock\LockBackendInterface'); $this->requestStack = new RequestStack(); $request = Request::createFromGlobals(); + $session = $this->getMock(SessionInterface::class); + $request->setSession($session); $this->requestStack->push($request); + $current_user = $this->getMock(AccountProxyInterface::class); - $this->tempStore = new SharedTempStore($this->keyValue, $this->lock, $this->owner, $this->requestStack, 604800); + $this->tempStore = new SharedTempStore($this->keyValue, $this->lock, $this->owner, $this->requestStack, $current_user, 604800); $this->ownObject = (object) [ 'data' => 'test_data', diff --git a/core/modules/user/user.module b/core/modules/user/user.module index 7c18443ef1..cbc9ee3722 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -566,8 +566,10 @@ function user_login_finalize(UserInterface $account) { // This is called before hook_user_login() in case one of those functions // fails or incorrectly does a redirect which would leave the old session // in place. - \Drupal::service('session')->migrate(); - \Drupal::service('session')->set('uid', $account->id()); + /** @var \Symfony\Component\HttpFoundation\Session\Session $session */ + $session = \Drupal::service('session'); + $session->migrate(TRUE); + $session->set('uid', $account->id()); \Drupal::moduleHandler()->invokeAll('user_login', [$account]); } diff --git a/core/tests/Drupal/KernelTests/Core/TempStore/TempStoreDatabaseTest.php b/core/tests/Drupal/KernelTests/Core/TempStore/TempStoreDatabaseTest.php index 929bfbec7d..1b6ac6abc1 100644 --- a/core/tests/Drupal/KernelTests/Core/TempStore/TempStoreDatabaseTest.php +++ b/core/tests/Drupal/KernelTests/Core/TempStore/TempStoreDatabaseTest.php @@ -3,6 +3,7 @@ namespace Drupal\KernelTests\Core\TempStore; use Drupal\Core\KeyValueStore\KeyValueExpirableFactory; +use Drupal\Core\Session\AccountProxyInterface; use Drupal\KernelTests\KernelTestBase; use Drupal\Core\TempStore\SharedTempStoreFactory; use Drupal\Core\Lock\DatabaseLockBackend; @@ -64,7 +65,15 @@ class TempStoreDatabaseTest extends KernelTestBase { public function testSharedTempStore() { // Create a key/value collection. $database = Database::getConnection(); - $factory = new SharedTempStoreFactory(new KeyValueExpirableFactory(\Drupal::getContainer()), new DatabaseLockBackend($database), $this->container->get('request_stack')); + // Mock the current user service so that isAnonymous returns FALSE. + $current_user = $this->prophesize(AccountProxyInterface::class); + $factory = new SharedTempStoreFactory( + new KeyValueExpirableFactory(\Drupal::getContainer()), + new DatabaseLockBackend($database), + $this->container->get('request_stack'), + $current_user->reveal(), + $this->container->get('session') + ); $collection = $this->randomMachineName(); // Create two mock users. diff --git a/core/tests/Drupal/Tests/Core/TempStore/SharedTempStoreFactoryTest.php b/core/tests/Drupal/Tests/Core/TempStore/SharedTempStoreFactoryTest.php new file mode 100644 index 0000000000..2874b35c83 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/TempStore/SharedTempStoreFactoryTest.php @@ -0,0 +1,48 @@ +prophesize(AccountProxyInterface::class)->reveal(); + $session = $this->prophesize(SessionInterface::class)->reveal(); + $container->set('current_user', $current_user); + $container->set('session', $session); + \Drupal::setContainer($container); + + $key_value_factory = $this->prophesize(KeyValueExpirableFactoryInterface::class)->reveal(); + $lock = $this->prophesize(LockBackendInterface::class)->reveal(); + $request_stack = $this->prophesize(RequestStack::class)->reveal(); + $store = new SharedTempStoreFactory($key_value_factory, $lock, $request_stack, 1000); + + $reflection_class = new \ReflectionClass(SharedTempStoreFactory::class); + + $current_user_property = $reflection_class->getProperty('currentUser'); + $current_user_property->setAccessible(TRUE); + $this->assertSame($current_user, $current_user_property->getValue($store)); + + $expire_property = $reflection_class->getProperty('expire'); + $expire_property->setAccessible(TRUE); + $this->assertSame(1000, $expire_property->getValue($store)); + } + +} diff --git a/core/tests/Drupal/Tests/Core/TempStore/SharedTempStoreTest.php b/core/tests/Drupal/Tests/Core/TempStore/SharedTempStoreTest.php index 3e519d184c..719a4686e0 100644 --- a/core/tests/Drupal/Tests/Core/TempStore/SharedTempStoreTest.php +++ b/core/tests/Drupal/Tests/Core/TempStore/SharedTempStoreTest.php @@ -2,6 +2,8 @@ namespace Drupal\Tests\Core\TempStore; +use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Session\AccountProxyInterface; use Drupal\Core\TempStore\Lock; use Drupal\Tests\UnitTestCase; use Drupal\Core\TempStore\SharedTempStore; @@ -9,6 +11,7 @@ use Drupal\Core\TempStore\TempStoreException; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Session\SessionInterface; /** * @coversDefaultClass \Drupal\Core\TempStore\SharedTempStore @@ -75,9 +78,12 @@ class SharedTempStoreTest extends UnitTestCase { $this->lock = $this->createMock('Drupal\Core\Lock\LockBackendInterface'); $this->requestStack = new RequestStack(); $request = Request::createFromGlobals(); + $session = $this->getMock(SessionInterface::class); + $request->setSession($session); $this->requestStack->push($request); + $current_user = $this->getMock(AccountProxyInterface::class); - $this->tempStore = new SharedTempStore($this->keyValue, $this->lock, $this->owner, $this->requestStack, 604800); + $this->tempStore = new SharedTempStore($this->keyValue, $this->lock, $this->owner, $this->requestStack, $current_user, 604800); $this->ownObject = (object) [ 'data' => 'test_data', @@ -411,4 +417,25 @@ class SharedTempStoreTest extends UnitTestCase { $this->assertSame($unserializable_request, $request_stack->pop()); } + /** + * @group legacy + * @expectedDeprecation Drupal\Core\TempStore\SharedTempStore::__construct() now requires the current_user service to be injected. See https://www.drupal.org/node/3006268 + */ + public function testLegacyConstructor() { + $container = new ContainerBuilder(); + $current_user = $this->getMock(AccountProxyInterface::class); + $container->set('current_user', $current_user); + \Drupal::setContainer($container); + $store = new SharedTempStore($this->keyValue, $this->lock, 2, $this->requestStack, 1000); + $reflection_class = new \ReflectionClass(SharedTempStore::class); + + $current_user_property = $reflection_class->getProperty('currentUser'); + $current_user_property->setAccessible(TRUE); + $this->assertSame($current_user, $current_user_property->getValue($store)); + + $expire_property = $reflection_class->getProperty('expire'); + $expire_property->setAccessible(TRUE); + $this->assertSame(1000, $expire_property->getValue($store)); + } + }