diff --git a/core/core.services.yml b/core/core.services.yml index 737009bac531e5a7d2ab388e2834ab1e025f19b3..fd8fc04c6807ab30e69693952549441da682ee9d 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -41,6 +41,7 @@ parameters: exposedHeaders: false maxAge: false supportsCredentials: false + tempstore.expire: 604800 services: # Simple cache contexts, directly derived from the request context. cache_context.ip: @@ -1650,3 +1651,13 @@ services: messenger: class: Drupal\Core\Messenger\Messenger arguments: ['@session.flash_bag', '@page_cache_kill_switch'] + tempstore.private: + class: Drupal\Core\TempStore\PrivateTempStoreFactory + arguments: ['@keyvalue.expirable', '@lock', '@current_user', '@request_stack', '%tempstore.expire%'] + tags: + - { name: backend_overridable } + tempstore.shared: + class: Drupal\Core\TempStore\SharedTempStoreFactory + arguments: ['@keyvalue.expirable', '@lock', '@request_stack', '%tempstore.expire%'] + tags: + - { name: backend_overridable } diff --git a/core/lib/Drupal/Core/Session/SessionManager.php b/core/lib/Drupal/Core/Session/SessionManager.php index 95495cfd033dffc9a418f50635cde3e25c6bad12..607103109dbcc809520b236665fd2ca0b4377670 100644 --- a/core/lib/Drupal/Core/Session/SessionManager.php +++ b/core/lib/Drupal/Core/Session/SessionManager.php @@ -120,8 +120,9 @@ public function start() { if (empty($result)) { // Randomly generate a session identifier for this request. This is - // necessary because \Drupal\user\SharedTempStoreFactory::get() wants to - // know the future session ID of a lazily started session in advance. + // 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 diff --git a/core/lib/Drupal/Core/TempStore/PrivateTempStore.php b/core/lib/Drupal/Core/TempStore/PrivateTempStore.php new file mode 100644 index 0000000000000000000000000000000000000000..f9d10347050450e388e790deeb425aeaa01cfde3 --- /dev/null +++ b/core/lib/Drupal/Core/TempStore/PrivateTempStore.php @@ -0,0 +1,213 @@ +storage = $storage; + $this->lockBackend = $lock_backend; + $this->currentUser = $current_user; + $this->requestStack = $request_stack; + $this->expire = $expire; + } + + /** + * Retrieves a value from this PrivateTempStore for a given key. + * + * @param string $key + * The key of the data to retrieve. + * + * @return mixed + * The data associated with the key, or NULL if the key does not exist. + */ + public function get($key) { + $key = $this->createkey($key); + if (($object = $this->storage->get($key)) && ($object->owner == $this->getOwner())) { + return $object->data; + } + } + + /** + * Stores a particular key/value pair in this PrivateTempStore. + * + * @param string $key + * The key of the data to store. + * @param mixed $value + * The data to store. + * + * @throws \Drupal\Core\TempStore\TempStoreException + * Thrown when a lock for the backend storage could not be acquired. + */ + public function set($key, $value) { + $key = $this->createkey($key); + if (!$this->lockBackend->acquire($key)) { + $this->lockBackend->wait($key); + if (!$this->lockBackend->acquire($key)) { + throw new TempStoreException("Couldn't acquire lock to update item '$key' in '{$this->storage->getCollectionName()}' temporary storage."); + } + } + + $value = (object) [ + 'owner' => $this->getOwner(), + 'data' => $value, + 'updated' => (int) $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME'), + ]; + $this->storage->setWithExpire($key, $value, $this->expire); + $this->lockBackend->release($key); + } + + /** + * Returns the metadata associated with a particular key/value pair. + * + * @param string $key + * The key of the data to store. + * + * @return mixed + * An object with the owner and updated time if the key has a value, or + * NULL otherwise. + */ + public function getMetadata($key) { + $key = $this->createkey($key); + // Fetch the key/value pair and its metadata. + $object = $this->storage->get($key); + if ($object) { + // Don't keep the data itself in memory. + unset($object->data); + return $object; + } + } + + /** + * Deletes data from the store for a given key and releases the lock on it. + * + * @param string $key + * The key of the data to delete. + * + * @return bool + * TRUE if the object was deleted or does not exist, FALSE if it exists but + * is not owned by $this->owner. + * + * @throws \Drupal\Core\TempStore\TempStoreException + * Thrown when a lock for the backend storage could not be acquired. + */ + public function delete($key) { + $key = $this->createkey($key); + if (!$object = $this->storage->get($key)) { + return TRUE; + } + elseif ($object->owner != $this->getOwner()) { + return FALSE; + } + if (!$this->lockBackend->acquire($key)) { + $this->lockBackend->wait($key); + if (!$this->lockBackend->acquire($key)) { + throw new TempStoreException("Couldn't acquire lock to delete item '$key' from '{$this->storage->getCollectionName()}' temporary storage."); + } + } + $this->storage->delete($key); + $this->lockBackend->release($key); + return TRUE; + } + + /** + * Ensures that the key is unique for a user. + * + * @param string $key + * The key. + * + * @return string + * The unique key for the user. + */ + protected function createkey($key) { + return $this->getOwner() . ':' . $key; + } + + /** + * Gets the current owner based on the current user or the session ID. + * + * @return string + * The owner. + */ + protected function getOwner() { + return $this->currentUser->id() ?: $this->requestStack->getCurrentRequest()->getSession()->getId(); + } + +} diff --git a/core/lib/Drupal/Core/TempStore/PrivateTempStoreFactory.php b/core/lib/Drupal/Core/TempStore/PrivateTempStoreFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..b80b825b497b3c7752c1f3a47939cee8604f2a27 --- /dev/null +++ b/core/lib/Drupal/Core/TempStore/PrivateTempStoreFactory.php @@ -0,0 +1,88 @@ +storageFactory = $storage_factory; + $this->lockBackend = $lock_backend; + $this->currentUser = $current_user; + $this->requestStack = $request_stack; + $this->expire = $expire; + } + + /** + * Creates a PrivateTempStore. + * + * @param string $collection + * The collection name to use for this key/value store. This is typically + * a shared namespace or module name, e.g. 'views', 'entity', etc. + * + * @return \Drupal\Core\TempStore\PrivateTempStore + * An instance of the key/value store. + */ + public function get($collection) { + // Store the data for this collection in the database. + $storage = $this->storageFactory->get("tempstore.private.$collection"); + return new PrivateTempStore($storage, $this->lockBackend, $this->currentUser, $this->requestStack, $this->expire); + } + +} diff --git a/core/lib/Drupal/Core/TempStore/SharedTempStore.php b/core/lib/Drupal/Core/TempStore/SharedTempStore.php new file mode 100644 index 0000000000000000000000000000000000000000..c131a80fd210d0b49b7d84bfb9b9e908942f1b76 --- /dev/null +++ b/core/lib/Drupal/Core/TempStore/SharedTempStore.php @@ -0,0 +1,279 @@ +storage = $storage; + $this->lockBackend = $lock_backend; + $this->owner = $owner; + $this->requestStack = $request_stack; + $this->expire = $expire; + } + + /** + * Retrieves a value from this SharedTempStore for a given key. + * + * @param string $key + * The key of the data to retrieve. + * + * @return mixed + * The data associated with the key, or NULL if the key does not exist. + */ + public function get($key) { + if ($object = $this->storage->get($key)) { + return $object->data; + } + } + + /** + * Retrieves a value from this SharedTempStore for a given key. + * + * Only returns the value if the value is owned by $this->owner. + * + * @param string $key + * The key of the data to retrieve. + * + * @return mixed + * The data associated with the key, or NULL if the key does not exist. + */ + public function getIfOwner($key) { + if (($object = $this->storage->get($key)) && ($object->owner == $this->owner)) { + return $object->data; + } + } + + /** + * Stores a particular key/value pair only if the key doesn't already exist. + * + * @param string $key + * The key of the data to check and store. + * @param mixed $value + * The data to store. + * + * @return bool + * TRUE if the data was set, or FALSE if it already existed. + */ + public function setIfNotExists($key, $value) { + $value = (object) [ + 'owner' => $this->owner, + 'data' => $value, + 'updated' => (int) $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME'), + ]; + return $this->storage->setWithExpireIfNotExists($key, $value, $this->expire); + } + + /** + * Stores a particular key/value pair in this SharedTempStore. + * + * Only stores the given key/value pair if it does not exist yet or is owned + * by $this->owner. + * + * @param string $key + * The key of the data to store. + * @param mixed $value + * The data to store. + * + * @return bool + * TRUE if the data was set, or FALSE if it already exists and is not owned + * by $this->user. + * + * @throws \Drupal\Core\TempStore\TempStoreException + * Thrown when a lock for the backend storage could not be acquired. + */ + public function setIfOwner($key, $value) { + if ($this->setIfNotExists($key, $value)) { + return TRUE; + } + + if (($object = $this->storage->get($key)) && ($object->owner == $this->owner)) { + $this->set($key, $value); + return TRUE; + } + + return FALSE; + } + + /** + * Stores a particular key/value pair in this SharedTempStore. + * + * @param string $key + * The key of the data to store. + * @param mixed $value + * The data to store. + * + * @throws \Drupal\Core\TempStore\TempStoreException + * Thrown when a lock for the backend storage could not be acquired. + */ + public function set($key, $value) { + if (!$this->lockBackend->acquire($key)) { + $this->lockBackend->wait($key); + if (!$this->lockBackend->acquire($key)) { + throw new TempStoreException("Couldn't acquire lock to update item '$key' in '{$this->storage->getCollectionName()}' temporary storage."); + } + } + + $value = (object) [ + 'owner' => $this->owner, + 'data' => $value, + 'updated' => (int) $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME'), + ]; + $this->storage->setWithExpire($key, $value, $this->expire); + $this->lockBackend->release($key); + } + + /** + * Returns the metadata associated with a particular key/value pair. + * + * @param string $key + * The key of the data to store. + * + * @return mixed + * An object with the owner and updated time if the key has a value, or + * NULL otherwise. + */ + public function getMetadata($key) { + // Fetch the key/value pair and its metadata. + $object = $this->storage->get($key); + if ($object) { + // Don't keep the data itself in memory. + unset($object->data); + return $object; + } + } + + /** + * Deletes data from the store for a given key and releases the lock on it. + * + * @param string $key + * The key of the data to delete. + * + * @throws \Drupal\Core\TempStore\TempStoreException + * Thrown when a lock for the backend storage could not be acquired. + */ + public function delete($key) { + if (!$this->lockBackend->acquire($key)) { + $this->lockBackend->wait($key); + if (!$this->lockBackend->acquire($key)) { + throw new TempStoreException("Couldn't acquire lock to delete item '$key' from {$this->storage->getCollectionName()} temporary storage."); + } + } + $this->storage->delete($key); + $this->lockBackend->release($key); + } + + /** + * Deletes data from the store for a given key and releases the lock on it. + * + * Only delete the given key if it is owned by $this->owner. + * + * @param string $key + * The key of the data to delete. + * + * @return bool + * TRUE if the object was deleted or does not exist, FALSE if it exists but + * is not owned by $this->owner. + * + * @throws \Drupal\Core\TempStore\TempStoreException + * Thrown when a lock for the backend storage could not be acquired. + */ + public function deleteIfOwner($key) { + if (!$object = $this->storage->get($key)) { + return TRUE; + } + elseif ($object->owner == $this->owner) { + $this->delete($key); + return TRUE; + } + + return FALSE; + } + +} diff --git a/core/lib/Drupal/Core/TempStore/SharedTempStoreFactory.php b/core/lib/Drupal/Core/TempStore/SharedTempStoreFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..90e816a7ddc3bb9d2ebf6f5209f6fc70d23538b3 --- /dev/null +++ b/core/lib/Drupal/Core/TempStore/SharedTempStoreFactory.php @@ -0,0 +1,87 @@ +storageFactory = $storage_factory; + $this->lockBackend = $lock_backend; + $this->requestStack = $request_stack; + $this->expire = $expire; + } + + /** + * Creates a SharedTempStore for the current user or anonymous session. + * + * @param string $collection + * The collection name to use for this key/value store. This is typically + * a shared namespace or module name, e.g. 'views', 'entity', etc. + * @param mixed $owner + * (optional) The owner of this SharedTempStore. By default, the + * SharedTempStore is owned by the currently authenticated user, or by the + * active anonymous session if no user is logged in. + * + * @return \Drupal\Core\TempStore\SharedTempStore + * An instance of the key/value store. + */ + public function get($collection, $owner = NULL) { + // 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(); + } + + // 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); + } + +} diff --git a/core/modules/user/src/TempStoreException.php b/core/lib/Drupal/Core/TempStore/TempStoreException.php similarity index 53% rename from core/modules/user/src/TempStoreException.php rename to core/lib/Drupal/Core/TempStore/TempStoreException.php index 1f9c79b0e46bfa65a013d07ae19d6c7658ab831e..53c3a5935f75287a17aaf36597ca085a7d18513d 100644 --- a/core/modules/user/src/TempStoreException.php +++ b/core/lib/Drupal/Core/TempStore/TempStoreException.php @@ -1,11 +1,11 @@ get('entity_type.manager'), $container->get('date.formatter'), $container->get('module_handler'), - $container->get('user.private_tempstore') + $container->get('tempstore.private') ); } diff --git a/core/modules/comment/src/Form/ConfirmDeleteMultiple.php b/core/modules/comment/src/Form/ConfirmDeleteMultiple.php index 83f188949ebd75f644914b810d74fb1c062126d6..704439393342f5c3893ace49391c534d7c315457 100644 --- a/core/modules/comment/src/Form/ConfirmDeleteMultiple.php +++ b/core/modules/comment/src/Form/ConfirmDeleteMultiple.php @@ -3,7 +3,7 @@ namespace Drupal\comment\Form; use Drupal\comment\CommentStorageInterface; -use Drupal\user\PrivateTempStoreFactory; +use Drupal\Core\TempStore\PrivateTempStoreFactory; use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Url; @@ -19,7 +19,7 @@ class ConfirmDeleteMultiple extends ConfirmFormBase { /** * The tempstore factory. * - * @var \Drupal\user\PrivateTempStoreFactory + * @var \Drupal\Core\TempStore\PrivateTempStoreFactory */ protected $tempStoreFactory; @@ -42,7 +42,7 @@ class ConfirmDeleteMultiple extends ConfirmFormBase { * * @param \Drupal\comment\CommentStorageInterface $comment_storage * The comment storage. - * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory + * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory * The tempstore factory. */ public function __construct(CommentStorageInterface $comment_storage, PrivateTempStoreFactory $temp_store_factory) { @@ -56,7 +56,7 @@ public function __construct(CommentStorageInterface $comment_storage, PrivateTem public static function create(ContainerInterface $container) { return new static( $container->get('entity.manager')->getStorage('comment'), - $container->get('user.private_tempstore') + $container->get('tempstore.private') ); } diff --git a/core/modules/comment/src/Plugin/Action/DeleteComment.php b/core/modules/comment/src/Plugin/Action/DeleteComment.php index 675b02180965faf95d99fa4d9ba054ed49264172..5c6941dd735005584cc5dd8f4e4d5d6818556ec8 100644 --- a/core/modules/comment/src/Plugin/Action/DeleteComment.php +++ b/core/modules/comment/src/Plugin/Action/DeleteComment.php @@ -5,7 +5,7 @@ use Drupal\Core\Action\ActionBase; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Session\AccountInterface; -use Drupal\user\PrivateTempStoreFactory; +use Drupal\Core\TempStore\PrivateTempStoreFactory; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -23,7 +23,7 @@ class DeleteComment extends ActionBase implements ContainerFactoryPluginInterfac /** * The tempstore object. * - * @var \Drupal\user\PrivateTempStore + * @var \Drupal\Core\TempStore\PrivateTempStore */ protected $tempStore; @@ -43,7 +43,7 @@ class DeleteComment extends ActionBase implements ContainerFactoryPluginInterfac * The plugin ID for the plugin instance. * @param array $plugin_definition * The plugin implementation definition. - * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory + * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory * The tempstore factory. * @param \Drupal\Core\Session\AccountInterface $current_user * The current user. @@ -62,7 +62,7 @@ public static function create(ContainerInterface $container, array $configuratio $configuration, $plugin_id, $plugin_definition, - $container->get('user.private_tempstore'), + $container->get('tempstore.private'), $container->get('current_user') ); } diff --git a/core/modules/image/src/Controller/QuickEditImageController.php b/core/modules/image/src/Controller/QuickEditImageController.php index aa49bc7d3c39268143b79186c6e7d6dd176deef6..5fc28ba2eb00cb8bb6d81c716c53c76212052e7e 100644 --- a/core/modules/image/src/Controller/QuickEditImageController.php +++ b/core/modules/image/src/Controller/QuickEditImageController.php @@ -10,7 +10,7 @@ use Drupal\Core\Render\Element\StatusMessages; use Drupal\Core\Render\RendererInterface; use Drupal\image\Plugin\Field\FieldType\ImageItem; -use Drupal\user\PrivateTempStoreFactory; +use Drupal\Core\TempStore\PrivateTempStoreFactory; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; @@ -23,7 +23,7 @@ class QuickEditImageController extends ControllerBase { /** * Stores The Quick Edit tempstore. * - * @var \Drupal\user\PrivateTempStore + * @var \Drupal\Core\TempStore\PrivateTempStore */ protected $tempStore; @@ -48,7 +48,7 @@ class QuickEditImageController extends ControllerBase { * The renderer. * @param \Drupal\Core\Image\ImageFactory $image_factory * The image factory. - * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory + * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory * The tempstore factory. */ public function __construct(RendererInterface $renderer, ImageFactory $image_factory, PrivateTempStoreFactory $temp_store_factory) { @@ -64,7 +64,7 @@ public static function create(ContainerInterface $container) { return new static( $container->get('renderer'), $container->get('image.factory'), - $container->get('user.private_tempstore') + $container->get('tempstore.private') ); } diff --git a/core/modules/layout_builder/layout_builder.services.yml b/core/modules/layout_builder/layout_builder.services.yml index 67f1aa1b4b7104738bfe18e60f7cd6353974552d..db6a1c13b36882c17cdef9436028940c04c9a286 100644 --- a/core/modules/layout_builder/layout_builder.services.yml +++ b/core/modules/layout_builder/layout_builder.services.yml @@ -1,7 +1,7 @@ services: layout_builder.tempstore_repository: class: Drupal\layout_builder\LayoutTempstoreRepository - arguments: ['@user.shared_tempstore'] + arguments: ['@tempstore.shared'] access_check.entity.layout: class: Drupal\layout_builder\Access\LayoutSectionAccessCheck tags: diff --git a/core/modules/layout_builder/src/LayoutTempstoreRepository.php b/core/modules/layout_builder/src/LayoutTempstoreRepository.php index 5e1a18d36ad3ee654c72fb0942dc3d8d2b61c234..0fa9d738bf0c9720868075b032aad93cf284b496 100644 --- a/core/modules/layout_builder/src/LayoutTempstoreRepository.php +++ b/core/modules/layout_builder/src/LayoutTempstoreRepository.php @@ -2,7 +2,7 @@ namespace Drupal\layout_builder; -use Drupal\user\SharedTempStoreFactory; +use Drupal\Core\TempStore\SharedTempStoreFactory; /** * Provides a mechanism for loading layouts from tempstore. @@ -14,14 +14,14 @@ class LayoutTempstoreRepository implements LayoutTempstoreRepositoryInterface { /** * The shared tempstore factory. * - * @var \Drupal\user\SharedTempStoreFactory + * @var \Drupal\Core\TempStore\SharedTempStoreFactory */ protected $tempStoreFactory; /** * LayoutTempstoreRepository constructor. * - * @param \Drupal\user\SharedTempStoreFactory $temp_store_factory + * @param \Drupal\Core\TempStore\SharedTempStoreFactory $temp_store_factory * The shared tempstore factory. */ public function __construct(SharedTempStoreFactory $temp_store_factory) { @@ -67,7 +67,7 @@ public function delete(SectionStorageInterface $section_storage) { * @param \Drupal\layout_builder\SectionStorageInterface $section_storage * The section storage. * - * @return \Drupal\user\SharedTempStore + * @return \Drupal\Core\TempStore\SharedTempStore * The tempstore. */ protected function getTempstore(SectionStorageInterface $section_storage) { diff --git a/core/modules/layout_builder/tests/src/Unit/LayoutTempstoreRepositoryTest.php b/core/modules/layout_builder/tests/src/Unit/LayoutTempstoreRepositoryTest.php index 75502d5f2eef081c9fe953fbdaf26422eaefd400..b7702c118424f99f9ddb4384b9c7676dd1fd2426 100644 --- a/core/modules/layout_builder/tests/src/Unit/LayoutTempstoreRepositoryTest.php +++ b/core/modules/layout_builder/tests/src/Unit/LayoutTempstoreRepositoryTest.php @@ -5,8 +5,8 @@ use Drupal\layout_builder\LayoutTempstoreRepository; use Drupal\layout_builder\SectionStorageInterface; use Drupal\Tests\UnitTestCase; -use Drupal\user\SharedTempStore; -use Drupal\user\SharedTempStoreFactory; +use Drupal\Core\TempStore\SharedTempStore; +use Drupal\Core\TempStore\SharedTempStoreFactory; /** * @coversDefaultClass \Drupal\layout_builder\LayoutTempstoreRepository diff --git a/core/modules/media/src/Form/MediaDeleteMultipleConfirmForm.php b/core/modules/media/src/Form/MediaDeleteMultipleConfirmForm.php index 616a567e3c185522a557a77328dd48c3905219d9..66598873f46d461cff0dfd0b1bdc85f72e013361 100644 --- a/core/modules/media/src/Form/MediaDeleteMultipleConfirmForm.php +++ b/core/modules/media/src/Form/MediaDeleteMultipleConfirmForm.php @@ -6,7 +6,7 @@ use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Url; -use Drupal\user\PrivateTempStoreFactory; +use Drupal\Core\TempStore\PrivateTempStoreFactory; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; @@ -27,7 +27,7 @@ class MediaDeleteMultipleConfirmForm extends ConfirmFormBase { /** * The tempstore factory. * - * @var \Drupal\user\PrivateTempStoreFactory + * @var \Drupal\Core\TempStore\PrivateTempStoreFactory */ protected $tempStoreFactory; @@ -41,7 +41,7 @@ class MediaDeleteMultipleConfirmForm extends ConfirmFormBase { /** * Constructs a MediaDeleteMultipleConfirmForm form object. * - * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory + * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory * The tempstore factory. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $manager * The entity type manager. @@ -56,7 +56,7 @@ public function __construct(PrivateTempStoreFactory $temp_store_factory, EntityT */ public static function create(ContainerInterface $container) { return new static( - $container->get('user.private_tempstore'), + $container->get('tempstore.private'), $container->get('entity_type.manager') ); } diff --git a/core/modules/node/node.services.yml b/core/modules/node/node.services.yml index 2ff32c3e7238b1d1a18b43cf67e96e976ce50e50..f94a4bba05a1702bafe4e3ff10b6a2befd2fc6d4 100644 --- a/core/modules/node/node.services.yml +++ b/core/modules/node/node.services.yml @@ -30,7 +30,7 @@ services: - { name: event_subscriber } node_preview: class: Drupal\node\ParamConverter\NodePreviewConverter - arguments: ['@user.private_tempstore'] + arguments: ['@tempstore.private'] tags: - { name: paramconverter } lazy: true diff --git a/core/modules/node/src/Form/DeleteMultiple.php b/core/modules/node/src/Form/DeleteMultiple.php index 8bc8f0685d7e737527dacda54036e043fe4010f2..ef2c89fc917c165eca5329e7f08d1fac805a54f7 100644 --- a/core/modules/node/src/Form/DeleteMultiple.php +++ b/core/modules/node/src/Form/DeleteMultiple.php @@ -6,7 +6,7 @@ use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Url; -use Drupal\user\PrivateTempStoreFactory; +use Drupal\Core\TempStore\PrivateTempStoreFactory; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; @@ -27,7 +27,7 @@ class DeleteMultiple extends ConfirmFormBase { /** * The tempstore factory. * - * @var \Drupal\user\PrivateTempStoreFactory + * @var \Drupal\Core\TempStore\PrivateTempStoreFactory */ protected $tempStoreFactory; @@ -41,7 +41,7 @@ class DeleteMultiple extends ConfirmFormBase { /** * Constructs a DeleteMultiple form object. * - * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory + * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory * The tempstore factory. * @param \Drupal\Core\Entity\EntityManagerInterface $manager * The entity manager. @@ -56,7 +56,7 @@ public function __construct(PrivateTempStoreFactory $temp_store_factory, EntityM */ public static function create(ContainerInterface $container) { return new static( - $container->get('user.private_tempstore'), + $container->get('tempstore.private'), $container->get('entity.manager') ); } diff --git a/core/modules/node/src/NodeForm.php b/core/modules/node/src/NodeForm.php index 0513d1f7585f1fda933e7a91c2b44debd0f52ea7..0ad34ad24200aebba7fbf7a9c2f7da00f9e47f65 100644 --- a/core/modules/node/src/NodeForm.php +++ b/core/modules/node/src/NodeForm.php @@ -8,7 +8,7 @@ use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Session\AccountInterface; -use Drupal\user\PrivateTempStoreFactory; +use Drupal\Core\TempStore\PrivateTempStoreFactory; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -21,7 +21,7 @@ class NodeForm extends ContentEntityForm { /** * The tempstore factory. * - * @var \Drupal\user\PrivateTempStoreFactory + * @var \Drupal\Core\TempStore\PrivateTempStoreFactory */ protected $tempStoreFactory; @@ -37,7 +37,7 @@ class NodeForm extends ContentEntityForm { * * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * The entity manager. - * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory + * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory * The factory for the temp store object. * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info * The entity type bundle service. @@ -58,7 +58,7 @@ public function __construct(EntityManagerInterface $entity_manager, PrivateTempS public static function create(ContainerInterface $container) { return new static( $container->get('entity.manager'), - $container->get('user.private_tempstore'), + $container->get('tempstore.private'), $container->get('entity_type.bundle.info'), $container->get('datetime.time'), $container->get('current_user') diff --git a/core/modules/node/src/ParamConverter/NodePreviewConverter.php b/core/modules/node/src/ParamConverter/NodePreviewConverter.php index b75d0626299e10c42eaad36206b2cb0a697a071d..a40301a404bb045ca3859bc626a0c7b630e5ab30 100644 --- a/core/modules/node/src/ParamConverter/NodePreviewConverter.php +++ b/core/modules/node/src/ParamConverter/NodePreviewConverter.php @@ -2,7 +2,7 @@ namespace Drupal\node\ParamConverter; -use Drupal\user\PrivateTempStoreFactory; +use Drupal\Core\TempStore\PrivateTempStoreFactory; use Symfony\Component\Routing\Route; use Drupal\Core\ParamConverter\ParamConverterInterface; @@ -14,14 +14,14 @@ class NodePreviewConverter implements ParamConverterInterface { /** * Stores the tempstore factory. * - * @var \Drupal\user\PrivateTempStoreFactory + * @var \Drupal\Core\TempStore\PrivateTempStoreFactory */ protected $tempStoreFactory; /** * Constructs a new NodePreviewConverter. * - * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory + * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory * The factory for the temp store object. */ public function __construct(PrivateTempStoreFactory $temp_store_factory) { diff --git a/core/modules/node/src/Plugin/Action/DeleteNode.php b/core/modules/node/src/Plugin/Action/DeleteNode.php index c4d1aebe0b3503213bb7c8dbe0be76ca26447314..19f8a7af2dbf05cffa777c974426ce5e3ce5df95 100644 --- a/core/modules/node/src/Plugin/Action/DeleteNode.php +++ b/core/modules/node/src/Plugin/Action/DeleteNode.php @@ -5,7 +5,7 @@ use Drupal\Core\Action\ActionBase; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Session\AccountInterface; -use Drupal\user\PrivateTempStoreFactory; +use Drupal\Core\TempStore\PrivateTempStoreFactory; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -23,7 +23,7 @@ class DeleteNode extends ActionBase implements ContainerFactoryPluginInterface { /** * The tempstore object. * - * @var \Drupal\user\SharedTempStore + * @var \Drupal\Core\TempStore\SharedTempStore */ protected $tempStore; @@ -43,7 +43,7 @@ class DeleteNode extends ActionBase implements ContainerFactoryPluginInterface { * The plugin ID for the plugin instance. * @param mixed $plugin_definition * The plugin implementation definition. - * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory + * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory * The tempstore factory. * @param \Drupal\Core\Session\AccountInterface $current_user * Current user. @@ -63,7 +63,7 @@ public static function create(ContainerInterface $container, array $configuratio $configuration, $plugin_id, $plugin_definition, - $container->get('user.private_tempstore'), + $container->get('tempstore.private'), $container->get('current_user') ); } diff --git a/core/modules/quickedit/src/Form/QuickEditFieldForm.php b/core/modules/quickedit/src/Form/QuickEditFieldForm.php index a9e0671a12b12e04fc139ecb37505d6c4512f59f..c092167a794799c9ef2b24fbc157658f66a7e3ee 100644 --- a/core/modules/quickedit/src/Form/QuickEditFieldForm.php +++ b/core/modules/quickedit/src/Form/QuickEditFieldForm.php @@ -10,7 +10,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element; use Drupal\Core\Entity\Entity\EntityFormDisplay; -use Drupal\user\PrivateTempStoreFactory; +use Drupal\Core\TempStore\PrivateTempStoreFactory; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Validator\Validator\ValidatorInterface; @@ -24,7 +24,7 @@ class QuickEditFieldForm extends FormBase { /** * Stores the tempstore factory. * - * @var \Drupal\user\PrivateTempStoreFactory + * @var \Drupal\Core\TempStore\PrivateTempStoreFactory */ protected $tempStoreFactory; @@ -52,7 +52,7 @@ class QuickEditFieldForm extends FormBase { /** * Constructs a new EditFieldForm. * - * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory + * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory * The tempstore factory. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler. @@ -73,7 +73,7 @@ public function __construct(PrivateTempStoreFactory $temp_store_factory, ModuleH */ public static function create(ContainerInterface $container) { return new static( - $container->get('user.private_tempstore'), + $container->get('tempstore.private'), $container->get('module_handler'), $container->get('entity.manager')->getStorage('node_type'), $container->get('typed_data_manager')->getValidator() diff --git a/core/modules/quickedit/src/QuickEditController.php b/core/modules/quickedit/src/QuickEditController.php index 2b1e4b230a2a8a3646c3347f2b69d0ccbc347fbe..0f470b1e474d0c684f3f94b52d45708fd53b5636 100644 --- a/core/modules/quickedit/src/QuickEditController.php +++ b/core/modules/quickedit/src/QuickEditController.php @@ -5,7 +5,7 @@ use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Form\FormState; use Drupal\Core\Render\RendererInterface; -use Drupal\user\PrivateTempStoreFactory; +use Drupal\Core\TempStore\PrivateTempStoreFactory; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -25,7 +25,7 @@ class QuickEditController extends ControllerBase { /** * The PrivateTempStore factory. * - * @var \Drupal\user\PrivateTempStoreFactory + * @var \Drupal\Core\TempStore\PrivateTempStoreFactory */ protected $tempStoreFactory; @@ -53,7 +53,7 @@ class QuickEditController extends ControllerBase { /** * Constructs a new QuickEditController. * - * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory + * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory * The PrivateTempStore factory. * @param \Drupal\quickedit\MetadataGeneratorInterface $metadata_generator * The in-place editing metadata generator. @@ -74,7 +74,7 @@ public function __construct(PrivateTempStoreFactory $temp_store_factory, Metadat */ public static function create(ContainerInterface $container) { return new static( - $container->get('user.private_tempstore'), + $container->get('tempstore.private'), $container->get('quickedit.metadata.generator'), $container->get('quickedit.editor.selector'), $container->get('renderer') diff --git a/core/modules/user/src/Form/UserMultipleCancelConfirm.php b/core/modules/user/src/Form/UserMultipleCancelConfirm.php index 985d19918d3761163a1ec3e559f85f3a6f05ace4..b4487c2656a016a8b62751c727cb6acc7639ec42 100644 --- a/core/modules/user/src/Form/UserMultipleCancelConfirm.php +++ b/core/modules/user/src/Form/UserMultipleCancelConfirm.php @@ -6,7 +6,7 @@ use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Url; -use Drupal\user\PrivateTempStoreFactory; +use Drupal\Core\TempStore\PrivateTempStoreFactory; use Drupal\user\UserStorageInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -20,7 +20,7 @@ class UserMultipleCancelConfirm extends ConfirmFormBase { /** * The temp store factory. * - * @var \Drupal\user\PrivateTempStoreFactory + * @var \Drupal\Core\TempStore\PrivateTempStoreFactory */ protected $tempStoreFactory; @@ -41,7 +41,7 @@ class UserMultipleCancelConfirm extends ConfirmFormBase { /** * Constructs a new UserMultipleCancelConfirm. * - * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory + * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory * The temp store factory. * @param \Drupal\user\UserStorageInterface $user_storage * The user storage. @@ -59,7 +59,7 @@ public function __construct(PrivateTempStoreFactory $temp_store_factory, UserSto */ public static function create(ContainerInterface $container) { return new static( - $container->get('user.private_tempstore'), + $container->get('tempstore.private'), $container->get('entity.manager')->getStorage('user'), $container->get('entity.manager') ); diff --git a/core/modules/user/src/Plugin/Action/CancelUser.php b/core/modules/user/src/Plugin/Action/CancelUser.php index 60ae15686e0293ab1c188bfd0d7598c84c6d25ea..e9726b436d7a686229c3420c724b02ef2d3d9b30 100644 --- a/core/modules/user/src/Plugin/Action/CancelUser.php +++ b/core/modules/user/src/Plugin/Action/CancelUser.php @@ -5,7 +5,7 @@ use Drupal\Core\Action\ActionBase; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Session\AccountInterface; -use Drupal\user\PrivateTempStoreFactory; +use Drupal\Core\TempStore\PrivateTempStoreFactory; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -23,7 +23,7 @@ class CancelUser extends ActionBase implements ContainerFactoryPluginInterface { /** * The tempstore factory. * - * @var \Drupal\user\PrivateTempStoreFactory + * @var \Drupal\Core\TempStore\PrivateTempStoreFactory */ protected $tempStoreFactory; @@ -43,7 +43,7 @@ class CancelUser extends ActionBase implements ContainerFactoryPluginInterface { * The plugin ID for the plugin instance. * @param mixed $plugin_definition * The plugin implementation definition. - * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory + * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory * The tempstore factory. * @param \Drupal\Core\Session\AccountInterface $current_user * Current user. @@ -63,7 +63,7 @@ public static function create(ContainerInterface $container, array $configuratio $configuration, $plugin_id, $plugin_definition, - $container->get('user.private_tempstore'), + $container->get('tempstore.private'), $container->get('current_user') ); } diff --git a/core/modules/user/src/PrivateTempStore.php b/core/modules/user/src/PrivateTempStore.php index 786ff1e44948a13d117ccb2d882777cf1f857cd1..8b0bc43fb0c47c262bfe44db8ed247c84451f11d 100644 --- a/core/modules/user/src/PrivateTempStore.php +++ b/core/modules/user/src/PrivateTempStore.php @@ -2,212 +2,25 @@ namespace Drupal\user; -use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface; -use Drupal\Core\Lock\LockBackendInterface; -use Drupal\Core\Session\AccountProxyInterface; -use Symfony\Component\HttpFoundation\RequestStack; +use Drupal\Core\TempStore\PrivateTempStore as CorePrivateTempStore; + +@trigger_error('\Drupal\user\PrivateTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\PrivateTempStore instead. See https://www.drupal.org/node/2935639.', E_USER_DEPRECATED); + +/** + * In order to preserve BC alias the core exception. + */ +if (!class_exists('\Drupal\user\TempStoreException')) { + class_alias('\Drupal\Core\TempStore\TempStoreException', '\Drupal\user\TempStoreException'); +} /** * Stores and retrieves temporary data for a given owner. * - * A PrivateTempStore can be used to make temporary, non-cache data available - * across requests. The data for the PrivateTempStore is stored in one - * key/value collection. PrivateTempStore data expires automatically after a - * given timeframe. + * @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0. + * Use \Drupal\Core\TempStore\PrivateTempStore instead. * - * The PrivateTempStore is different from a cache, because the data in it is not - * yet saved permanently and so it cannot be rebuilt. Typically, the - * PrivateTempStore might be used to store work in progress that is later saved - * permanently elsewhere, e.g. autosave data, multistep forms, or in-progress - * changes to complex configuration that are not ready to be saved. - * - * The PrivateTempStore differs from the SharedTempStore in that all keys are - * ensured to be unique for a particular user and users can never share data. If - * you want to be able to share data between users or use it for locking, use - * \Drupal\user\SharedTempStore. + * @see \Drupal\Core\TempStore\PrivateTempStore + * @see https://www.drupal.org/node/2935639 */ -class PrivateTempStore { - - /** - * The key/value storage object used for this data. - * - * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface - */ - protected $storage; - - /** - * The lock object used for this data. - * - * @var \Drupal\Core\Lock\LockBackendInterface - */ - protected $lockBackend; - - /** - * The current user. - * - * @var \Drupal\Core\Session\AccountProxyInterface - */ - protected $currentUser; - - /** - * The request stack. - * - * @var \Symfony\Component\HttpFoundation\RequestStack - */ - protected $requestStack; - - /** - * The time to live for items in seconds. - * - * By default, data is stored for one week (604800 seconds) before expiring. - * - * @var int - */ - protected $expire; - - /** - * Constructs a new object for accessing data from a key/value store. - * - * @param \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface $storage - * The key/value storage object used for this data. Each storage object - * represents a particular collection of data and will contain any number - * of key/value pairs. - * @param \Drupal\Core\Lock\LockBackendInterface $lock_backend - * The lock object used for this data. - * @param \Drupal\Core\Session\AccountProxyInterface $current_user - * The current user account. - * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack - * The request stack. - * @param int $expire - * The time to live for items, in seconds. - */ - public function __construct(KeyValueStoreExpirableInterface $storage, LockBackendInterface $lock_backend, AccountProxyInterface $current_user, RequestStack $request_stack, $expire = 604800) { - $this->storage = $storage; - $this->lockBackend = $lock_backend; - $this->currentUser = $current_user; - $this->requestStack = $request_stack; - $this->expire = $expire; - } - - /** - * Retrieves a value from this PrivateTempStore for a given key. - * - * @param string $key - * The key of the data to retrieve. - * - * @return mixed - * The data associated with the key, or NULL if the key does not exist. - */ - public function get($key) { - $key = $this->createkey($key); - if (($object = $this->storage->get($key)) && ($object->owner == $this->getOwner())) { - return $object->data; - } - } - - /** - * Stores a particular key/value pair in this PrivateTempStore. - * - * @param string $key - * The key of the data to store. - * @param mixed $value - * The data to store. - * - * @throws \Drupal\user\TempStoreException - * Thrown when a lock for the backend storage could not be acquired. - */ - public function set($key, $value) { - $key = $this->createkey($key); - if (!$this->lockBackend->acquire($key)) { - $this->lockBackend->wait($key); - if (!$this->lockBackend->acquire($key)) { - throw new TempStoreException("Couldn't acquire lock to update item '$key' in '{$this->storage->getCollectionName()}' temporary storage."); - } - } - - $value = (object) [ - 'owner' => $this->getOwner(), - 'data' => $value, - 'updated' => (int) $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME'), - ]; - $this->storage->setWithExpire($key, $value, $this->expire); - $this->lockBackend->release($key); - } - - /** - * Returns the metadata associated with a particular key/value pair. - * - * @param string $key - * The key of the data to store. - * - * @return mixed - * An object with the owner and updated time if the key has a value, or - * NULL otherwise. - */ - public function getMetadata($key) { - $key = $this->createkey($key); - // Fetch the key/value pair and its metadata. - $object = $this->storage->get($key); - if ($object) { - // Don't keep the data itself in memory. - unset($object->data); - return $object; - } - } - - /** - * Deletes data from the store for a given key and releases the lock on it. - * - * @param string $key - * The key of the data to delete. - * - * @return bool - * TRUE if the object was deleted or does not exist, FALSE if it exists but - * is not owned by $this->owner. - * - * @throws \Drupal\user\TempStoreException - * Thrown when a lock for the backend storage could not be acquired. - */ - public function delete($key) { - $key = $this->createkey($key); - if (!$object = $this->storage->get($key)) { - return TRUE; - } - elseif ($object->owner != $this->getOwner()) { - return FALSE; - } - if (!$this->lockBackend->acquire($key)) { - $this->lockBackend->wait($key); - if (!$this->lockBackend->acquire($key)) { - throw new TempStoreException("Couldn't acquire lock to delete item '$key' from '{$this->storage->getCollectionName()}' temporary storage."); - } - } - $this->storage->delete($key); - $this->lockBackend->release($key); - return TRUE; - } - - /** - * Ensures that the key is unique for a user. - * - * @param string $key - * The key. - * - * @return string - * The unique key for the user. - */ - protected function createkey($key) { - return $this->getOwner() . ':' . $key; - } - - /** - * Gets the current owner based on the current user or the session ID. - * - * @return string - * The owner. - */ - protected function getOwner() { - return $this->currentUser->id() ?: $this->requestStack->getCurrentRequest()->getSession()->getId(); - } - +class PrivateTempStore extends CorePrivateTempStore { } diff --git a/core/modules/user/src/PrivateTempStoreFactory.php b/core/modules/user/src/PrivateTempStoreFactory.php index e51faf342daa83176d32a99ee7a6b822593c2831..21d6bd1c4af8c65112c56d4c79ba47631e8b78a5 100644 --- a/core/modules/user/src/PrivateTempStoreFactory.php +++ b/core/modules/user/src/PrivateTempStoreFactory.php @@ -2,72 +2,20 @@ namespace Drupal\user; -use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface; -use Drupal\Core\Lock\LockBackendInterface; -use Drupal\Core\Session\AccountProxyInterface; -use Symfony\Component\HttpFoundation\RequestStack; +use Drupal\Core\TempStore\PrivateTempStoreFactory as CorePrivateTempStoreFactory; + +@trigger_error('\Drupal\user\PrivateTempStoreFactory is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\PrivateTempStoreFactory instead. See https://www.drupal.org/node/2935639.', E_USER_DEPRECATED); /** * Creates a PrivateTempStore object for a given collection. + * + * @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0. + * Use \Drupal\Core\TempStore\PrivateTempStoreFactory instead. + * + * @see \Drupal\Core\TempStore\PrivateTempStoreFactory + * @see https://www.drupal.org/node/2935639 */ -class PrivateTempStoreFactory { - - /** - * The storage factory creating the backend to store the data. - * - * @var \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface - */ - protected $storageFactory; - - /** - * The lock object used for this data. - * - * @var \Drupal\Core\Lock\LockBackendInterface - */ - protected $lockBackend; - - /** - * The current user. - * - * @var \Drupal\Core\Session\AccountProxyInterface - */ - protected $currentUser; - - /** - * The request stack. - * - * @var \Symfony\Component\HttpFoundation\RequestStack - */ - protected $requestStack; - - /** - * The time to live for items in seconds. - * - * @var int - */ - protected $expire; - - /** - * Constructs a Drupal\user\PrivateTempStoreFactory object. - * - * @param \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface $storage_factory - * The key/value store factory. - * @param \Drupal\Core\Lock\LockBackendInterface $lock_backend - * The lock object used for this data. - * @param \Drupal\Core\Session\AccountProxyInterface $current_user - * The current account. - * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack - * The request stack. - * @param int $expire - * The time to live for items, in seconds. - */ - public function __construct(KeyValueExpirableFactoryInterface $storage_factory, LockBackendInterface $lock_backend, AccountProxyInterface $current_user, RequestStack $request_stack, $expire = 604800) { - $this->storageFactory = $storage_factory; - $this->lockBackend = $lock_backend; - $this->currentUser = $current_user; - $this->requestStack = $request_stack; - $this->expire = $expire; - } +class PrivateTempStoreFactory extends CorePrivateTempStoreFactory { /** * Creates a PrivateTempStore. diff --git a/core/modules/user/src/SharedTempStore.php b/core/modules/user/src/SharedTempStore.php index 91a622bbef76720b5889ea0343289e361e3fdc20..18021104626b28f700c83f9a3df6be4369015a67 100644 --- a/core/modules/user/src/SharedTempStore.php +++ b/core/modules/user/src/SharedTempStore.php @@ -2,278 +2,25 @@ namespace Drupal\user; -use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface; -use Drupal\Core\Lock\LockBackendInterface; -use Symfony\Component\HttpFoundation\RequestStack; +use Drupal\Core\TempStore\SharedTempStore as CoreSharedTempStore; + +@trigger_error('\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.', E_USER_DEPRECATED); + +/** + * In order to preserve BC alias the core exception. + */ +if (!class_exists('\Drupal\user\TempStoreException')) { + class_alias('\Drupal\Core\TempStore\TempStoreException', '\Drupal\user\TempStoreException'); +} /** * Stores and retrieves temporary data for a given owner. * - * A SharedTempStore can be used to make temporary, non-cache data available - * across requests. The data for the SharedTempStore is stored in one key/value - * collection. SharedTempStore data expires automatically after a given - * timeframe. - * - * The SharedTempStore is different from a cache, because the data in it is not - * yet saved permanently and so it cannot be rebuilt. Typically, the - * SharedTempStore might be used to store work in progress that is later saved - * permanently elsewhere, e.g. autosave data, multistep forms, or in-progress - * changes to complex configuration that are not ready to be saved. - * - * Each SharedTempStore belongs to a particular owner (e.g. a user, session, or - * process). Multiple owners may use the same key/value collection, and the - * owner is stored along with the key/value pair. + * @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0. + * Use \Drupal\Core\TempStore\SharedTempStore instead. * - * Every key is unique within the collection, so the SharedTempStore can check - * whether a particular key is already set by a different owner. This is - * useful for informing one owner that the data is already in use by another; - * for example, to let one user know that another user is in the process of - * editing certain data, or even to restrict other users from editing it at - * the same time. It is the responsibility of the implementation to decide - * when and whether one owner can use or update another owner's data. - * - * If you want to be able to ensure that the data belongs to the current user, - * use \Drupal\user\PrivateTempStore. + * @see \Drupal\Core\TempStore\SharedTempStore + * @see https://www.drupal.org/node/2935639 */ -class SharedTempStore { - - /** - * The key/value storage object used for this data. - * - * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface - */ - protected $storage; - - /** - * The lock object used for this data. - * - * @var \Drupal\Core\Lock\LockBackendInterface - */ - protected $lockBackend; - - /** - * The request stack. - * - * @var \Symfony\Component\HttpFoundation\RequestStack - */ - protected $requestStack; - - /** - * The owner key to store along with the data (e.g. a user or session ID). - * - * @var mixed - */ - protected $owner; - - /** - * The time to live for items in seconds. - * - * By default, data is stored for one week (604800 seconds) before expiring. - * - * @var int - */ - protected $expire; - - /** - * Constructs a new object for accessing data from a key/value store. - * - * @param \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface $storage - * The key/value storage object used for this data. Each storage object - * represents a particular collection of data and will contain any number - * of key/value pairs. - * @param \Drupal\Core\Lock\LockBackendInterface $lock_backend - * The lock object used for this data. - * @param mixed $owner - * 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 int $expire - * The time to live for items, in seconds. - */ - public function __construct(KeyValueStoreExpirableInterface $storage, LockBackendInterface $lock_backend, $owner, RequestStack $request_stack, $expire = 604800) { - $this->storage = $storage; - $this->lockBackend = $lock_backend; - $this->owner = $owner; - $this->requestStack = $request_stack; - $this->expire = $expire; - } - - /** - * Retrieves a value from this SharedTempStore for a given key. - * - * @param string $key - * The key of the data to retrieve. - * - * @return mixed - * The data associated with the key, or NULL if the key does not exist. - */ - public function get($key) { - if ($object = $this->storage->get($key)) { - return $object->data; - } - } - - /** - * Retrieves a value from this SharedTempStore for a given key. - * - * Only returns the value if the value is owned by $this->owner. - * - * @param string $key - * The key of the data to retrieve. - * - * @return mixed - * The data associated with the key, or NULL if the key does not exist. - */ - public function getIfOwner($key) { - if (($object = $this->storage->get($key)) && ($object->owner == $this->owner)) { - return $object->data; - } - } - - /** - * Stores a particular key/value pair only if the key doesn't already exist. - * - * @param string $key - * The key of the data to check and store. - * @param mixed $value - * The data to store. - * - * @return bool - * TRUE if the data was set, or FALSE if it already existed. - */ - public function setIfNotExists($key, $value) { - $value = (object) [ - 'owner' => $this->owner, - 'data' => $value, - 'updated' => (int) $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME'), - ]; - return $this->storage->setWithExpireIfNotExists($key, $value, $this->expire); - } - - /** - * Stores a particular key/value pair in this SharedTempStore. - * - * Only stores the given key/value pair if it does not exist yet or is owned - * by $this->owner. - * - * @param string $key - * The key of the data to store. - * @param mixed $value - * The data to store. - * - * @return bool - * TRUE if the data was set, or FALSE if it already exists and is not owned - * by $this->user. - * - * @throws \Drupal\user\TempStoreException - * Thrown when a lock for the backend storage could not be acquired. - */ - public function setIfOwner($key, $value) { - if ($this->setIfNotExists($key, $value)) { - return TRUE; - } - - if (($object = $this->storage->get($key)) && ($object->owner == $this->owner)) { - $this->set($key, $value); - return TRUE; - } - - return FALSE; - } - - /** - * Stores a particular key/value pair in this SharedTempStore. - * - * @param string $key - * The key of the data to store. - * @param mixed $value - * The data to store. - * - * @throws \Drupal\user\TempStoreException - * Thrown when a lock for the backend storage could not be acquired. - */ - public function set($key, $value) { - if (!$this->lockBackend->acquire($key)) { - $this->lockBackend->wait($key); - if (!$this->lockBackend->acquire($key)) { - throw new TempStoreException("Couldn't acquire lock to update item '$key' in '{$this->storage->getCollectionName()}' temporary storage."); - } - } - - $value = (object) [ - 'owner' => $this->owner, - 'data' => $value, - 'updated' => (int) $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME'), - ]; - $this->storage->setWithExpire($key, $value, $this->expire); - $this->lockBackend->release($key); - } - - /** - * Returns the metadata associated with a particular key/value pair. - * - * @param string $key - * The key of the data to store. - * - * @return mixed - * An object with the owner and updated time if the key has a value, or - * NULL otherwise. - */ - public function getMetadata($key) { - // Fetch the key/value pair and its metadata. - $object = $this->storage->get($key); - if ($object) { - // Don't keep the data itself in memory. - unset($object->data); - return $object; - } - } - - /** - * Deletes data from the store for a given key and releases the lock on it. - * - * @param string $key - * The key of the data to delete. - * - * @throws \Drupal\user\TempStoreException - * Thrown when a lock for the backend storage could not be acquired. - */ - public function delete($key) { - if (!$this->lockBackend->acquire($key)) { - $this->lockBackend->wait($key); - if (!$this->lockBackend->acquire($key)) { - throw new TempStoreException("Couldn't acquire lock to delete item '$key' from {$this->storage->getCollectionName()} temporary storage."); - } - } - $this->storage->delete($key); - $this->lockBackend->release($key); - } - - /** - * Deletes data from the store for a given key and releases the lock on it. - * - * Only delete the given key if it is owned by $this->owner. - * - * @param string $key - * The key of the data to delete. - * - * @return bool - * TRUE if the object was deleted or does not exist, FALSE if it exists but - * is not owned by $this->owner. - * - * @throws \Drupal\user\TempStoreException - * Thrown when a lock for the backend storage could not be acquired. - */ - public function deleteIfOwner($key) { - if (!$object = $this->storage->get($key)) { - return TRUE; - } - elseif ($object->owner == $this->owner) { - $this->delete($key); - return TRUE; - } - - return FALSE; - } - +class SharedTempStore extends CoreSharedTempStore { } diff --git a/core/modules/user/src/SharedTempStoreFactory.php b/core/modules/user/src/SharedTempStoreFactory.php index 29f868320198224c3b0f69f4b5c305fbe04d7f4c..c955f07f7af18cbd6d1bda5b90d1957e55574bf9 100644 --- a/core/modules/user/src/SharedTempStoreFactory.php +++ b/core/modules/user/src/SharedTempStoreFactory.php @@ -2,61 +2,20 @@ namespace Drupal\user; -use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface; -use Drupal\Core\Lock\LockBackendInterface; -use Symfony\Component\HttpFoundation\RequestStack; +use Drupal\Core\TempStore\SharedTempStoreFactory as CoreSharedTempStoreFactory; + +@trigger_error('\Drupal\user\SharedTempStoreFactory is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\SharedTempStoreFactory instead. See https://www.drupal.org/node/2935639.', E_USER_DEPRECATED); /** * Creates a shared temporary storage for a collection. + * + * @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0. + * Use \Drupal\Core\TempStore\SharedTempStoreFactory instead. + * + * @see \Drupal\Core\TempStore\SharedTempStoreFactory + * @see https://www.drupal.org/node/2935639 */ -class SharedTempStoreFactory { - - /** - * The storage factory creating the backend to store the data. - * - * @var \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface - */ - protected $storageFactory; - - /** - * The lock object used for this data. - * - * @var \Drupal\Core\Lock\LockBackendInterface - */ - protected $lockBackend; - - /** - * The request stack. - * - * @var \Symfony\Component\HttpFoundation\RequestStack - */ - protected $requestStack; - - /** - * The time to live for items in seconds. - * - * @var int - */ - protected $expire; - - /** - * Constructs a Drupal\user\SharedTempStoreFactory object. - * - * @param \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface $storage_factory - * The key/value store factory. - * @param \Drupal\Core\Lock\LockBackendInterface $lock_backend - * The lock object used for this data. - * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack - * The request stack. - * @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) { - $this->storageFactory = $storage_factory; - $this->lockBackend = $lock_backend; - $this->requestStack = $request_stack; - $this->expire = $expire; - } +class SharedTempStoreFactory extends CoreSharedTempStoreFactory { /** * Creates a SharedTempStore for the current user or anonymous session. diff --git a/core/modules/user/src/UserServiceProvider.php b/core/modules/user/src/UserServiceProvider.php index 49537e56a6100dda9ccdc6e10fb1aa5a899ae70f..d43604c7680af761bf8ca4b380d7ab9819949cf4 100644 --- a/core/modules/user/src/UserServiceProvider.php +++ b/core/modules/user/src/UserServiceProvider.php @@ -1,8 +1,20 @@ hasParameter('user.tempstore.expire')) { + @trigger_error('The container parameter "user.tempstore.expire" is deprecated. Use "tempstore.expire" instead. See https://www.drupal.org/node/2935639.', E_USER_DEPRECATED); + $container->setParameter('tempstore.expire', $container->getParameter('user.tempstore.expire')); + } + } + +} diff --git a/core/modules/user/tests/src/Kernel/TempStoreDatabaseTest.php b/core/modules/user/tests/src/Kernel/TempStoreDatabaseTest.php index b3d7ff1e1a1b3065d80681b3ef3a244416a2c7d6..ae9d4cb13eec5e39f15600c11d2267fdd1c399c4 100644 --- a/core/modules/user/tests/src/Kernel/TempStoreDatabaseTest.php +++ b/core/modules/user/tests/src/Kernel/TempStoreDatabaseTest.php @@ -12,7 +12,8 @@ * Tests the temporary object storage system. * * @group user - * @see \Drupal\Core\TempStore\TempStore. + * @group legacy + * @see \Drupal\user\SharedTempStore */ class TempStoreDatabaseTest extends KernelTestBase { @@ -67,6 +68,9 @@ protected function setUp() { /** * Tests the UserTempStore API. + * + * @expectedDeprecation \Drupal\user\SharedTempStoreFactory is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\SharedTempStoreFactory instead. See https://www.drupal.org/node/2935639. + * @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() { // Create a key/value collection. diff --git a/core/modules/user/tests/src/Kernel/UserServiceProviderTest.php b/core/modules/user/tests/src/Kernel/UserServiceProviderTest.php new file mode 100644 index 0000000000000000000000000000000000000000..38f1af1d4817f11253f1e73b960dcd728646d707 --- /dev/null +++ b/core/modules/user/tests/src/Kernel/UserServiceProviderTest.php @@ -0,0 +1,40 @@ +assertEquals(1000, $this->container->getParameter('tempstore.expire')); + } + + /** + * {@inheritdoc} + */ + public function register(ContainerBuilder $container) { + $container->setParameter('user.tempstore.expire', 1000); + parent::register($container); + } + +} diff --git a/core/modules/user/tests/src/Unit/PrivateTempStoreTest.php b/core/modules/user/tests/src/Unit/PrivateTempStoreTest.php index 8d188114ccde129540c430c4fcace2f6c9d3c8e1..be9376ad1dc1c4f2582340b49097dceb06d177ad 100644 --- a/core/modules/user/tests/src/Unit/PrivateTempStoreTest.php +++ b/core/modules/user/tests/src/Unit/PrivateTempStoreTest.php @@ -11,6 +11,9 @@ /** * @coversDefaultClass \Drupal\user\PrivateTempStore * @group user + * @group legacy + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled */ class PrivateTempStoreTest extends UnitTestCase { @@ -98,6 +101,7 @@ protected function setUp() { * Tests the get() method. * * @covers ::get + * @expectedDeprecation \Drupal\user\PrivateTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\PrivateTempStore instead. See https://www.drupal.org/node/2935639. */ public function testGet() { $this->keyValue->expects($this->at(0)) @@ -122,6 +126,7 @@ public function testGet() { * Tests the set() method with no lock available. * * @covers ::set + * @expectedDeprecation \Drupal\user\PrivateTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\PrivateTempStore instead. See https://www.drupal.org/node/2935639. */ public function testSetWithNoLockAvailable() { $this->lock->expects($this->at(0)) @@ -147,6 +152,7 @@ public function testSetWithNoLockAvailable() { * Tests a successful set() call. * * @covers ::set + * @expectedDeprecation \Drupal\user\PrivateTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\PrivateTempStore instead. See https://www.drupal.org/node/2935639. */ public function testSet() { $this->lock->expects($this->once()) @@ -170,6 +176,7 @@ public function testSet() { * Tests the getMetadata() method. * * @covers ::getMetadata + * @expectedDeprecation \Drupal\user\PrivateTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\PrivateTempStore instead. See https://www.drupal.org/node/2935639. */ public function testGetMetadata() { $this->keyValue->expects($this->at(0)) @@ -194,6 +201,7 @@ public function testGetMetadata() { * Tests the locking in the delete() method. * * @covers ::delete + * @expectedDeprecation \Drupal\user\PrivateTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\PrivateTempStore instead. See https://www.drupal.org/node/2935639. */ public function testDeleteLocking() { $this->keyValue->expects($this->once()) @@ -221,6 +229,7 @@ public function testDeleteLocking() { * Tests the delete() method with no lock available. * * @covers ::delete + * @expectedDeprecation \Drupal\user\PrivateTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\PrivateTempStore instead. See https://www.drupal.org/node/2935639. */ public function testDeleteWithNoLockAvailable() { $this->keyValue->expects($this->once()) @@ -250,6 +259,7 @@ public function testDeleteWithNoLockAvailable() { * Tests the delete() method. * * @covers ::delete + * @expectedDeprecation \Drupal\user\PrivateTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\PrivateTempStore instead. See https://www.drupal.org/node/2935639. */ public function testDelete() { $this->lock->expects($this->once()) diff --git a/core/modules/user/tests/src/Unit/SharedTempStoreTest.php b/core/modules/user/tests/src/Unit/SharedTempStoreTest.php index 33bde413e2509c1de4d2bbffcd97107f26e20812..6b5d4419681b7f8e677bfddbe42a4a6b53862c47 100644 --- a/core/modules/user/tests/src/Unit/SharedTempStoreTest.php +++ b/core/modules/user/tests/src/Unit/SharedTempStoreTest.php @@ -11,6 +11,9 @@ /** * @coversDefaultClass \Drupal\user\SharedTempStore * @group user + * @group legacy + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled */ class SharedTempStoreTest extends UnitTestCase { @@ -90,6 +93,7 @@ protected function setUp() { /** * @covers ::get + * @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 testGet() { $this->keyValue->expects($this->at(0)) @@ -109,6 +113,7 @@ public function testGet() { * Tests the getIfOwner() method. * * @covers ::getIfOwner + * @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 testGetIfOwner() { $this->keyValue->expects($this->at(0)) @@ -133,6 +138,7 @@ public function testGetIfOwner() { * Tests the set() method with no lock available. * * @covers ::set + * @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 testSetWithNoLockAvailable() { $this->lock->expects($this->at(0)) @@ -158,6 +164,7 @@ public function testSetWithNoLockAvailable() { * Tests a successful set() call. * * @covers ::set + * @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 testSet() { $this->lock->expects($this->once()) @@ -181,6 +188,7 @@ public function testSet() { * Tests the setIfNotExists() methods. * * @covers ::setIfNotExists + * @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 testSetIfNotExists() { $this->keyValue->expects($this->once()) @@ -195,6 +203,7 @@ public function testSetIfNotExists() { * Tests the setIfOwner() method when no key exists. * * @covers ::setIfOwner + * @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 testSetIfOwnerWhenNotExists() { $this->keyValue->expects($this->once()) @@ -208,6 +217,7 @@ public function testSetIfOwnerWhenNotExists() { * Tests the setIfOwner() method when a key already exists but no object. * * @covers ::setIfOwner + * @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 testSetIfOwnerNoObject() { $this->keyValue->expects($this->once()) @@ -226,6 +236,7 @@ public function testSetIfOwnerNoObject() { * Tests the setIfOwner() method with matching and non matching owners. * * @covers ::setIfOwner + * @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 testSetIfOwner() { $this->lock->expects($this->once()) @@ -250,6 +261,7 @@ public function testSetIfOwner() { * Tests the getMetadata() method. * * @covers ::getMetadata + * @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 testGetMetadata() { $this->keyValue->expects($this->at(0)) @@ -274,6 +286,7 @@ public function testGetMetadata() { * Tests the delete() method. * * @covers ::delete + * @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 testDelete() { $this->lock->expects($this->once()) @@ -297,6 +310,7 @@ public function testDelete() { * Tests the delete() method with no lock available. * * @covers ::delete + * @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 testDeleteWithNoLockAvailable() { $this->lock->expects($this->at(0)) @@ -322,6 +336,7 @@ public function testDeleteWithNoLockAvailable() { * Tests the deleteIfOwner() method. * * @covers ::deleteIfOwner + * @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 testDeleteIfOwner() { $this->lock->expects($this->once()) diff --git a/core/modules/user/user.services.yml b/core/modules/user/user.services.yml index c273cb34433cfc795ffdd94a2d9ac0608c70d924..aede2f462ba695e80750c320cfa1e326834f0ed0 100644 --- a/core/modules/user/user.services.yml +++ b/core/modules/user/user.services.yml @@ -50,12 +50,14 @@ services: arguments: ['@entity.manager', '@password'] user.private_tempstore: class: Drupal\user\PrivateTempStoreFactory - arguments: ['@keyvalue.expirable', '@lock', '@current_user', '@request_stack', '%user.tempstore.expire%'] + arguments: ['@keyvalue.expirable', '@lock', '@current_user', '@request_stack', '%tempstore.expire%'] + deprecated: The "%service_id%" service is deprecated. You should use the 'tempstore.private' service instead. See https://www.drupal.org/node/2935639. tags: - { name: backend_overridable } user.shared_tempstore: class: Drupal\user\SharedTempStoreFactory - arguments: ['@keyvalue.expirable', '@lock', '@request_stack', '%user.tempstore.expire%'] + arguments: ['@keyvalue.expirable', '@lock', '@request_stack', '%tempstore.expire%'] + deprecated: The "%service_id%" service is deprecated. You should use the 'tempstore.shared' service instead. See https://www.drupal.org/node/2935639. tags: - { name: backend_overridable } user.permissions: @@ -66,6 +68,3 @@ services: arguments: ['@current_user', '@entity.manager'] tags: - { name: 'context_provider' } - -parameters: - user.tempstore.expire: 604800 diff --git a/core/modules/views/src/Entity/View.php b/core/modules/views/src/Entity/View.php index b69234c3bcb9a0dab34d3d9ef55836c51e2a2368..c82d64c7b9dd650731186eb53a05a6315bc1fa2a 100644 --- a/core/modules/views/src/Entity/View.php +++ b/core/modules/views/src/Entity/View.php @@ -456,7 +456,7 @@ public static function preDelete(EntityStorageInterface $storage, array $entitie public static function postDelete(EntityStorageInterface $storage, array $entities) { parent::postDelete($storage, $entities); - $tempstore = \Drupal::service('user.shared_tempstore')->get('views'); + $tempstore = \Drupal::service('tempstore.shared')->get('views'); foreach ($entities as $entity) { $tempstore->delete($entity->id()); } diff --git a/core/modules/views_ui/src/Form/BreakLockForm.php b/core/modules/views_ui/src/Form/BreakLockForm.php index 464920b7b58b0c9ab85856f6e5b32b7a7eb90426..6e93848802a1aa21b16012523e483d767d44d6e9 100644 --- a/core/modules/views_ui/src/Form/BreakLockForm.php +++ b/core/modules/views_ui/src/Form/BreakLockForm.php @@ -5,7 +5,7 @@ use Drupal\Core\Entity\EntityConfirmFormBase; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Form\FormStateInterface; -use Drupal\user\SharedTempStoreFactory; +use Drupal\Core\TempStore\SharedTempStoreFactory; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -23,9 +23,9 @@ class BreakLockForm extends EntityConfirmFormBase { protected $entityManager; /** - * Stores the user tempstore. + * Stores the shared tempstore. * - * @var \Drupal\user\SharedTempStore + * @var \Drupal\Core\TempStore\SharedTempStore */ protected $tempStore; @@ -34,7 +34,7 @@ class BreakLockForm extends EntityConfirmFormBase { * * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * The Entity manager. - * @param \Drupal\user\SharedTempStoreFactory $temp_store_factory + * @param \Drupal\Core\TempStore\SharedTempStoreFactory $temp_store_factory * The factory for the temp store object. */ public function __construct(EntityManagerInterface $entity_manager, SharedTempStoreFactory $temp_store_factory) { @@ -48,7 +48,7 @@ public function __construct(EntityManagerInterface $entity_manager, SharedTempSt public static function create(ContainerInterface $container) { return new static( $container->get('entity.manager'), - $container->get('user.shared_tempstore') + $container->get('tempstore.shared') ); } diff --git a/core/modules/views_ui/src/ParamConverter/ViewUIConverter.php b/core/modules/views_ui/src/ParamConverter/ViewUIConverter.php index ad718ba5b1915df5427156af4015642026e6f9b8..fb19d18a392380f2189ce71e528a3a183804c48e 100644 --- a/core/modules/views_ui/src/ParamConverter/ViewUIConverter.php +++ b/core/modules/views_ui/src/ParamConverter/ViewUIConverter.php @@ -8,7 +8,7 @@ use Drupal\Core\Routing\AdminContext; use Symfony\Component\Routing\Route; use Drupal\Core\ParamConverter\ParamConverterInterface; -use Drupal\user\SharedTempStoreFactory; +use Drupal\Core\TempStore\SharedTempStoreFactory; use Drupal\views_ui\ViewUI; /** @@ -32,7 +32,7 @@ class ViewUIConverter extends AdminPathConfigEntityConverter implements ParamCon /** * Stores the tempstore factory. * - * @var \Drupal\user\SharedTempStoreFactory + * @var \Drupal\Core\TempStore\SharedTempStoreFactory */ protected $tempStoreFactory; @@ -41,7 +41,7 @@ class ViewUIConverter extends AdminPathConfigEntityConverter implements ParamCon * * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * The entity manager. - * @param \Drupal\user\SharedTempStoreFactory $temp_store_factory + * @param \Drupal\Core\TempStore\SharedTempStoreFactory $temp_store_factory * The factory for the temp store object. */ public function __construct(EntityManagerInterface $entity_manager, SharedTempStoreFactory $temp_store_factory, ConfigFactoryInterface $config_factory = NULL, AdminContext $admin_context = NULL) { diff --git a/core/modules/views_ui/src/ViewEditForm.php b/core/modules/views_ui/src/ViewEditForm.php index 9c1d58a52910f55aba02b11064e23e2ed71dcd81..cea587e5ca0b9fdc76c47829ff62ff33c2ae361b 100644 --- a/core/modules/views_ui/src/ViewEditForm.php +++ b/core/modules/views_ui/src/ViewEditForm.php @@ -11,7 +11,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\ElementInfoManagerInterface; use Drupal\Core\Url; -use Drupal\user\SharedTempStoreFactory; +use Drupal\Core\TempStore\SharedTempStoreFactory; use Drupal\views\Views; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\RequestStack; @@ -27,7 +27,7 @@ class ViewEditForm extends ViewFormBase { /** * The views temp store. * - * @var \Drupal\user\SharedTempStore + * @var \Drupal\Core\TempStore\SharedTempStore */ protected $tempStore; @@ -55,7 +55,7 @@ class ViewEditForm extends ViewFormBase { /** * Constructs a new ViewEditForm object. * - * @param \Drupal\user\SharedTempStoreFactory $temp_store_factory + * @param \Drupal\Core\TempStore\SharedTempStoreFactory $temp_store_factory * The factory for the temp store object. * @param \Symfony\Component\HttpFoundation\RequestStack $requestStack * The request stack object. @@ -76,7 +76,7 @@ public function __construct(SharedTempStoreFactory $temp_store_factory, RequestS */ public static function create(ContainerInterface $container) { return new static( - $container->get('user.shared_tempstore'), + $container->get('tempstore.shared'), $container->get('request_stack'), $container->get('date.formatter'), $container->get('element_info') diff --git a/core/modules/views_ui/src/ViewUI.php b/core/modules/views_ui/src/ViewUI.php index 2e7fabd62bf0c0413cdeda1ff53284043c61fe29..b2be658e24f4fc33828909d95cc7215299d5e369 100644 --- a/core/modules/views_ui/src/ViewUI.php +++ b/core/modules/views_ui/src/ViewUI.php @@ -48,7 +48,7 @@ class ViewUI implements ViewEntityInterface { * If this view is locked for editing. * * If this view is locked it will contain the result of - * \Drupal\user\SharedTempStore::getMetadata(). Which can be a stdClass or + * \Drupal\Core\TempStore\SharedTempStore::getMetadata(). Which can be a stdClass or * NULL. * * @var stdClass @@ -855,7 +855,7 @@ public function getFormProgress() { } /** - * Sets a cached view object in the user tempstore. + * Sets a cached view object in the shared tempstore. */ public function cacheSet() { if ($this->isLocked()) { @@ -878,7 +878,7 @@ public function cacheSet() { $executable->default_display = NULL; $executable->query = NULL; $executable->displayHandlers = NULL; - \Drupal::service('user.shared_tempstore')->get('views')->set($this->id(), $this); + \Drupal::service('tempstore.shared')->get('views')->set($this->id(), $this); } /** diff --git a/core/modules/views_ui/tests/src/Functional/CachedDataUITest.php b/core/modules/views_ui/tests/src/Functional/CachedDataUITest.php index 51e489d93e6c2236132bf0bf251a7771d91db183..c8da87ce5a306087d365605122a5993bc1074de0 100644 --- a/core/modules/views_ui/tests/src/Functional/CachedDataUITest.php +++ b/core/modules/views_ui/tests/src/Functional/CachedDataUITest.php @@ -3,7 +3,7 @@ namespace Drupal\Tests\views_ui\Functional; /** - * Tests the user tempstore cache in the UI. + * Tests the shared tempstore cache in the UI. * * @group views_ui */ @@ -17,12 +17,12 @@ class CachedDataUITest extends UITestBase { public static $testViews = ['test_view']; /** - * Tests the user tempstore views data in the UI. + * Tests the shared tempstore views data in the UI. */ public function testCacheData() { $views_admin_user_uid = $this->fullAdminUser->id(); - $temp_store = $this->container->get('user.shared_tempstore')->get('views'); + $temp_store = $this->container->get('tempstore.shared')->get('views'); // The view should not be locked. $this->assertEqual($temp_store->getMetadata('test_view'), NULL, 'The view is not locked.'); @@ -40,7 +40,7 @@ public function testCacheData() { // Cancel the view edit and make sure the cache is deleted. $this->drupalPostForm(NULL, [], t('Cancel')); - $this->assertEqual($temp_store->getMetadata('test_view'), NULL, 'User tempstore data has been removed.'); + $this->assertEqual($temp_store->getMetadata('test_view'), NULL, 'Shared tempstore data has been removed.'); // Test we are redirected to the view listing page. $this->assertUrl('admin/structure/views', [], 'Redirected back to the view listing page.'); diff --git a/core/modules/views_ui/views_ui.services.yml b/core/modules/views_ui/views_ui.services.yml index 27fa334c511a2325cc2844c3b63b6f23881d7f8e..adf87e61a2d834cf114007744384018b07a425a7 100644 --- a/core/modules/views_ui/views_ui.services.yml +++ b/core/modules/views_ui/views_ui.services.yml @@ -1,7 +1,7 @@ services: paramconverter.views_ui: class: Drupal\views_ui\ParamConverter\ViewUIConverter - arguments: ['@entity.manager', '@user.shared_tempstore', '@config.factory', '@router.admin_context'] + arguments: ['@entity.manager', '@tempstore.shared', '@config.factory', '@router.admin_context'] tags: - { name: paramconverter, priority: 10 } lazy: true diff --git a/core/tests/Drupal/KernelTests/Core/TempStore/TempStoreDatabaseTest.php b/core/tests/Drupal/KernelTests/Core/TempStore/TempStoreDatabaseTest.php new file mode 100644 index 0000000000000000000000000000000000000000..2abe70760cada5566f65810391b43f51301f20e1 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/TempStore/TempStoreDatabaseTest.php @@ -0,0 +1,139 @@ +installSchema('system', ['key_value_expire']); + + // Create several objects for testing. + for ($i = 0; $i <= 3; $i++) { + $this->objects[$i] = $this->randomObject(); + } + + } + + /** + * Tests the SharedTempStore API. + */ + public function testSharedTempStore() { + // Create a key/value collection. + $factory = new SharedTempStoreFactory(new KeyValueExpirableFactory(\Drupal::getContainer()), new DatabaseLockBackend(Database::getConnection()), $this->container->get('request_stack')); + $collection = $this->randomMachineName(); + + // Create two mock users. + for ($i = 0; $i <= 1; $i++) { + $users[$i] = mt_rand(500, 5000000); + + // Storing the SharedTempStore objects in a class member variable causes a + // fatal exception, because in that situation garbage collection is not + // triggered until the test class itself is destructed, after tearDown() + // has deleted the database tables. Store the objects locally instead. + $stores[$i] = $factory->get($collection, $users[$i]); + } + + $key = $this->randomMachineName(); + // Test that setIfNotExists() succeeds only the first time. + for ($i = 0; $i <= 1; $i++) { + // setIfNotExists() should be TRUE the first time (when $i is 0) and + // FALSE the second time (when $i is 1). + $this->assertEqual(!$i, $stores[0]->setIfNotExists($key, $this->objects[$i])); + $metadata = $stores[0]->getMetadata($key); + $this->assertEqual($users[0], $metadata->owner); + $this->assertIdenticalObject($this->objects[0], $stores[0]->get($key)); + // Another user should get the same result. + $metadata = $stores[1]->getMetadata($key); + $this->assertEqual($users[0], $metadata->owner); + $this->assertIdenticalObject($this->objects[0], $stores[1]->get($key)); + } + + // Remove the item and try to set it again. + $stores[0]->delete($key); + $stores[0]->setIfNotExists($key, $this->objects[1]); + // This time it should succeed. + $this->assertIdenticalObject($this->objects[1], $stores[0]->get($key)); + + // This user can update the object. + $stores[0]->set($key, $this->objects[2]); + $this->assertIdenticalObject($this->objects[2], $stores[0]->get($key)); + // The object is the same when another user loads it. + $this->assertIdenticalObject($this->objects[2], $stores[1]->get($key)); + + // This user should be allowed to get, update, delete. + $this->assertTrue($stores[0]->getIfOwner($key) instanceof \stdClass); + $this->assertTrue($stores[0]->setIfOwner($key, $this->objects[1])); + $this->assertTrue($stores[0]->deleteIfOwner($key)); + + // Another user can update the object and become the owner. + $stores[1]->set($key, $this->objects[3]); + $this->assertIdenticalObject($this->objects[3], $stores[0]->get($key)); + $this->assertIdenticalObject($this->objects[3], $stores[1]->get($key)); + $metadata = $stores[1]->getMetadata($key); + $this->assertEqual($users[1], $metadata->owner); + + // The first user should be informed that the second now owns the data. + $metadata = $stores[0]->getMetadata($key); + $this->assertEqual($users[1], $metadata->owner); + + // The first user should no longer be allowed to get, update, delete. + $this->assertNull($stores[0]->getIfOwner($key)); + $this->assertFalse($stores[0]->setIfOwner($key, $this->objects[1])); + $this->assertFalse($stores[0]->deleteIfOwner($key)); + + // Now manually expire the item (this is not exposed by the API) and then + // assert it is no longer accessible. + db_update('key_value_expire') + ->fields(['expire' => REQUEST_TIME - 1]) + ->condition('collection', "tempstore.shared.$collection") + ->condition('name', $key) + ->execute(); + $this->assertFalse($stores[0]->get($key)); + $this->assertFalse($stores[1]->get($key)); + } + +} diff --git a/core/tests/Drupal/Tests/Core/TempStore/PrivateTempStoreTest.php b/core/tests/Drupal/Tests/Core/TempStore/PrivateTempStoreTest.php new file mode 100644 index 0000000000000000000000000000000000000000..0144919b2422ea975a02566335021b2410fad346 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/TempStore/PrivateTempStoreTest.php @@ -0,0 +1,281 @@ +keyValue = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface'); + $this->lock = $this->getMock('Drupal\Core\Lock\LockBackendInterface'); + $this->currentUser = $this->getMock('Drupal\Core\Session\AccountProxyInterface'); + $this->currentUser->expects($this->any()) + ->method('id') + ->willReturn(1); + + $this->requestStack = new RequestStack(); + $request = Request::createFromGlobals(); + $this->requestStack->push($request); + + $this->tempStore = new PrivateTempStore($this->keyValue, $this->lock, $this->currentUser, $this->requestStack, 604800); + + $this->ownObject = (object) [ + 'data' => 'test_data', + 'owner' => $this->currentUser->id(), + 'updated' => (int) $request->server->get('REQUEST_TIME'), + ]; + + // Clone the object but change the owner. + $this->otherObject = clone $this->ownObject; + $this->otherObject->owner = 2; + } + + + /** + * Tests the get() method. + * + * @covers ::get + */ + public function testGet() { + $this->keyValue->expects($this->at(0)) + ->method('get') + ->with('1:test_2') + ->will($this->returnValue(FALSE)); + $this->keyValue->expects($this->at(1)) + ->method('get') + ->with('1:test') + ->will($this->returnValue($this->ownObject)); + $this->keyValue->expects($this->at(2)) + ->method('get') + ->with('1:test') + ->will($this->returnValue($this->otherObject)); + + $this->assertNull($this->tempStore->get('test_2')); + $this->assertSame($this->ownObject->data, $this->tempStore->get('test')); + $this->assertNull($this->tempStore->get('test')); + } + + /** + * Tests the set() method with no lock available. + * + * @covers ::set + */ + public function testSetWithNoLockAvailable() { + $this->lock->expects($this->at(0)) + ->method('acquire') + ->with('1:test') + ->will($this->returnValue(FALSE)); + $this->lock->expects($this->at(1)) + ->method('wait') + ->with('1:test'); + $this->lock->expects($this->at(2)) + ->method('acquire') + ->with('1:test') + ->will($this->returnValue(FALSE)); + + $this->keyValue->expects($this->once()) + ->method('getCollectionName'); + + $this->setExpectedException(TempStoreException::class); + $this->tempStore->set('test', 'value'); + } + + /** + * Tests a successful set() call. + * + * @covers ::set + */ + public function testSet() { + $this->lock->expects($this->once()) + ->method('acquire') + ->with('1:test') + ->will($this->returnValue(TRUE)); + $this->lock->expects($this->never()) + ->method('wait'); + $this->lock->expects($this->once()) + ->method('release') + ->with('1:test'); + + $this->keyValue->expects($this->once()) + ->method('setWithExpire') + ->with('1:test', $this->ownObject, 604800); + + $this->tempStore->set('test', 'test_data'); + } + + /** + * Tests the getMetadata() method. + * + * @covers ::getMetadata + */ + public function testGetMetadata() { + $this->keyValue->expects($this->at(0)) + ->method('get') + ->with('1:test') + ->will($this->returnValue($this->ownObject)); + + $this->keyValue->expects($this->at(1)) + ->method('get') + ->with('1:test') + ->will($this->returnValue(FALSE)); + + $metadata = $this->tempStore->getMetadata('test'); + $this->assertObjectHasAttribute('owner', $metadata); + // Data should get removed. + $this->assertObjectNotHasAttribute('data', $metadata); + + $this->assertNull($this->tempStore->getMetadata('test')); + } + + /** + * Tests the locking in the delete() method. + * + * @covers ::delete + */ + public function testDeleteLocking() { + $this->keyValue->expects($this->once()) + ->method('get') + ->with('1:test') + ->will($this->returnValue($this->ownObject)); + $this->lock->expects($this->once()) + ->method('acquire') + ->with('1:test') + ->will($this->returnValue(TRUE)); + $this->lock->expects($this->never()) + ->method('wait'); + $this->lock->expects($this->once()) + ->method('release') + ->with('1:test'); + + $this->keyValue->expects($this->once()) + ->method('delete') + ->with('1:test'); + + $this->assertTrue($this->tempStore->delete('test')); + } + + /** + * Tests the delete() method with no lock available. + * + * @covers ::delete + */ + public function testDeleteWithNoLockAvailable() { + $this->keyValue->expects($this->once()) + ->method('get') + ->with('1:test') + ->will($this->returnValue($this->ownObject)); + $this->lock->expects($this->at(0)) + ->method('acquire') + ->with('1:test') + ->will($this->returnValue(FALSE)); + $this->lock->expects($this->at(1)) + ->method('wait') + ->with('1:test'); + $this->lock->expects($this->at(2)) + ->method('acquire') + ->with('1:test') + ->will($this->returnValue(FALSE)); + + $this->keyValue->expects($this->once()) + ->method('getCollectionName'); + + $this->setExpectedException(TempStoreException::class); + $this->tempStore->delete('test'); + } + + /** + * Tests the delete() method. + * + * @covers ::delete + */ + public function testDelete() { + $this->lock->expects($this->once()) + ->method('acquire') + ->with('1:test_2') + ->will($this->returnValue(TRUE)); + + $this->keyValue->expects($this->at(0)) + ->method('get') + ->with('1:test_1') + ->will($this->returnValue(FALSE)); + $this->keyValue->expects($this->at(1)) + ->method('get') + ->with('1:test_2') + ->will($this->returnValue($this->ownObject)); + $this->keyValue->expects($this->at(2)) + ->method('delete') + ->with('1:test_2'); + $this->keyValue->expects($this->at(3)) + ->method('get') + ->with('1:test_3') + ->will($this->returnValue($this->otherObject)); + + $this->assertTrue($this->tempStore->delete('test_1')); + $this->assertTrue($this->tempStore->delete('test_2')); + $this->assertFalse($this->tempStore->delete('test_3')); + } + +} diff --git a/core/tests/Drupal/Tests/Core/TempStore/SharedTempStoreTest.php b/core/tests/Drupal/Tests/Core/TempStore/SharedTempStoreTest.php new file mode 100644 index 0000000000000000000000000000000000000000..55a78b9a25a5615c44f0d1abfdf67c45d1b5b584 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/TempStore/SharedTempStoreTest.php @@ -0,0 +1,353 @@ +keyValue = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface'); + $this->lock = $this->getMock('Drupal\Core\Lock\LockBackendInterface'); + $this->requestStack = new RequestStack(); + $request = Request::createFromGlobals(); + $this->requestStack->push($request); + + $this->tempStore = new SharedTempStore($this->keyValue, $this->lock, $this->owner, $this->requestStack, 604800); + + $this->ownObject = (object) [ + 'data' => 'test_data', + 'owner' => $this->owner, + 'updated' => (int) $request->server->get('REQUEST_TIME'), + ]; + + // Clone the object but change the owner. + $this->otherObject = clone $this->ownObject; + $this->otherObject->owner = 2; + } + + /** + * @covers ::get + */ + public function testGet() { + $this->keyValue->expects($this->at(0)) + ->method('get') + ->with('test_2') + ->will($this->returnValue(FALSE)); + $this->keyValue->expects($this->at(1)) + ->method('get') + ->with('test') + ->will($this->returnValue($this->ownObject)); + + $this->assertNull($this->tempStore->get('test_2')); + $this->assertSame($this->ownObject->data, $this->tempStore->get('test')); + } + + /** + * Tests the getIfOwner() method. + * + * @covers ::getIfOwner + */ + public function testGetIfOwner() { + $this->keyValue->expects($this->at(0)) + ->method('get') + ->with('test_2') + ->will($this->returnValue(FALSE)); + $this->keyValue->expects($this->at(1)) + ->method('get') + ->with('test') + ->will($this->returnValue($this->ownObject)); + $this->keyValue->expects($this->at(2)) + ->method('get') + ->with('test') + ->will($this->returnValue($this->otherObject)); + + $this->assertNull($this->tempStore->getIfOwner('test_2')); + $this->assertSame($this->ownObject->data, $this->tempStore->getIfOwner('test')); + $this->assertNull($this->tempStore->getIfOwner('test')); + } + + /** + * Tests the set() method with no lock available. + * + * @covers ::set + */ + public function testSetWithNoLockAvailable() { + $this->lock->expects($this->at(0)) + ->method('acquire') + ->with('test') + ->will($this->returnValue(FALSE)); + $this->lock->expects($this->at(1)) + ->method('wait') + ->with('test'); + $this->lock->expects($this->at(2)) + ->method('acquire') + ->with('test') + ->will($this->returnValue(FALSE)); + + $this->keyValue->expects($this->once()) + ->method('getCollectionName'); + + $this->setExpectedException(TempStoreException::class); + $this->tempStore->set('test', 'value'); + } + + /** + * Tests a successful set() call. + * + * @covers ::set + */ + public function testSet() { + $this->lock->expects($this->once()) + ->method('acquire') + ->with('test') + ->will($this->returnValue(TRUE)); + $this->lock->expects($this->never()) + ->method('wait'); + $this->lock->expects($this->once()) + ->method('release') + ->with('test'); + + $this->keyValue->expects($this->once()) + ->method('setWithExpire') + ->with('test', $this->ownObject, 604800); + + $this->tempStore->set('test', 'test_data'); + } + + /** + * Tests the setIfNotExists() methods. + * + * @covers ::setIfNotExists + */ + public function testSetIfNotExists() { + $this->keyValue->expects($this->once()) + ->method('setWithExpireIfNotExists') + ->with('test', $this->ownObject, 604800) + ->will($this->returnValue(TRUE)); + + $this->assertTrue($this->tempStore->setIfNotExists('test', 'test_data')); + } + + /** + * Tests the setIfOwner() method when no key exists. + * + * @covers ::setIfOwner + */ + public function testSetIfOwnerWhenNotExists() { + $this->keyValue->expects($this->once()) + ->method('setWithExpireIfNotExists') + ->will($this->returnValue(TRUE)); + + $this->assertTrue($this->tempStore->setIfOwner('test', 'test_data')); + } + + /** + * Tests the setIfOwner() method when a key already exists but no object. + * + * @covers ::setIfOwner + */ + public function testSetIfOwnerNoObject() { + $this->keyValue->expects($this->once()) + ->method('setWithExpireIfNotExists') + ->will($this->returnValue(FALSE)); + + $this->keyValue->expects($this->once()) + ->method('get') + ->with('test') + ->will($this->returnValue(FALSE)); + + $this->assertFalse($this->tempStore->setIfOwner('test', 'test_data')); + } + + /** + * Tests the setIfOwner() method with matching and non matching owners. + * + * @covers ::setIfOwner + */ + public function testSetIfOwner() { + $this->lock->expects($this->once()) + ->method('acquire') + ->with('test') + ->will($this->returnValue(TRUE)); + + $this->keyValue->expects($this->exactly(2)) + ->method('setWithExpireIfNotExists') + ->will($this->returnValue(FALSE)); + + $this->keyValue->expects($this->exactly(2)) + ->method('get') + ->with('test') + ->will($this->onConsecutiveCalls($this->ownObject, $this->otherObject)); + + $this->assertTrue($this->tempStore->setIfOwner('test', 'test_data')); + $this->assertFalse($this->tempStore->setIfOwner('test', 'test_data')); + } + + /** + * Tests the getMetadata() method. + * + * @covers ::getMetadata + */ + public function testGetMetadata() { + $this->keyValue->expects($this->at(0)) + ->method('get') + ->with('test') + ->will($this->returnValue($this->ownObject)); + + $this->keyValue->expects($this->at(1)) + ->method('get') + ->with('test') + ->will($this->returnValue(FALSE)); + + $metadata = $this->tempStore->getMetadata('test'); + $this->assertObjectHasAttribute('owner', $metadata); + // Data should get removed. + $this->assertObjectNotHasAttribute('data', $metadata); + + $this->assertNull($this->tempStore->getMetadata('test')); + } + + /** + * Tests the delete() method. + * + * @covers ::delete + */ + public function testDelete() { + $this->lock->expects($this->once()) + ->method('acquire') + ->with('test') + ->will($this->returnValue(TRUE)); + $this->lock->expects($this->never()) + ->method('wait'); + $this->lock->expects($this->once()) + ->method('release') + ->with('test'); + + $this->keyValue->expects($this->once()) + ->method('delete') + ->with('test'); + + $this->tempStore->delete('test'); + } + + /** + * Tests the delete() method with no lock available. + * + * @covers ::delete + */ + public function testDeleteWithNoLockAvailable() { + $this->lock->expects($this->at(0)) + ->method('acquire') + ->with('test') + ->will($this->returnValue(FALSE)); + $this->lock->expects($this->at(1)) + ->method('wait') + ->with('test'); + $this->lock->expects($this->at(2)) + ->method('acquire') + ->with('test') + ->will($this->returnValue(FALSE)); + + $this->keyValue->expects($this->once()) + ->method('getCollectionName'); + + $this->setExpectedException(TempStoreException::class); + $this->tempStore->delete('test'); + } + + /** + * Tests the deleteIfOwner() method. + * + * @covers ::deleteIfOwner + */ + public function testDeleteIfOwner() { + $this->lock->expects($this->once()) + ->method('acquire') + ->with('test_2') + ->will($this->returnValue(TRUE)); + + $this->keyValue->expects($this->at(0)) + ->method('get') + ->with('test_1') + ->will($this->returnValue(FALSE)); + $this->keyValue->expects($this->at(1)) + ->method('get') + ->with('test_2') + ->will($this->returnValue($this->ownObject)); + $this->keyValue->expects($this->at(2)) + ->method('delete') + ->with('test_2'); + $this->keyValue->expects($this->at(3)) + ->method('get') + ->with('test_3') + ->will($this->returnValue($this->otherObject)); + + $this->assertTrue($this->tempStore->deleteIfOwner('test_1')); + $this->assertTrue($this->tempStore->deleteIfOwner('test_2')); + $this->assertFalse($this->tempStore->deleteIfOwner('test_3')); + } + +}