SharedTempStore.php in Drupal 9
Same filename and directory in other branches
Namespace
Drupal\Core\TempStoreFile
core/lib/Drupal/Core/TempStore/SharedTempStore.phpView source
<?php
namespace Drupal\Core\TempStore;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
use Drupal\Core\Lock\LockBackendInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* 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.
*
* 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\Core\TempStore\PrivateTempStore.
*/
class SharedTempStore {
use DependencySerializationTrait;
/**
* 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 current user.
*
* @var \Drupal\Core\Session\AccountProxyInterface
*/
protected $currentUser;
/**
* 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 \Drupal\Core\Session\AccountProxyInterface $current_user
* The current user.
* @param int $expire
* The time to live for items, in seconds.
*/
public function __construct(KeyValueStoreExpirableInterface $storage, LockBackendInterface $lock_backend, $owner, RequestStack $request_stack, $current_user = NULL, $expire = 604800) {
$this->storage = $storage;
$this->lockBackend = $lock_backend;
$this->owner = $owner;
$this->requestStack = $request_stack;
if (!$current_user instanceof AccountProxyInterface) {
@trigger_error('Calling ' . __METHOD__ . '() without the $current_user argument is deprecated in drupal:9.2.0 and will be required in drupal:10.0.0. See https://www.drupal.org/node/3006268', E_USER_DEPRECATED);
if (is_int($current_user)) {
// If the $current_user argument is numeric then this object has been
// instantiated with the old constructor signature.
$expire = $current_user;
}
$current_user = \Drupal::currentUser();
}
$this->currentUser = $current_user;
$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
->getMainRequest()->server
->get('REQUEST_TIME'),
];
$this
->ensureAnonymousSession();
$set = $this->storage
->setWithExpireIfNotExists($key, $value, $this->expire);
return $set;
}
/**
* 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
->getMainRequest()->server
->get('REQUEST_TIME'),
];
$this
->ensureAnonymousSession();
$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 \Drupal\Core\TempStore\Lock|null
* 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 new Lock($object->owner, $object->updated);
}
}
/**
* 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;
}
/**
* Stores the owner in the session if the user is anonymous.
*
* This method should be called when a value is set.
*/
protected function ensureAnonymousSession() {
// If this is being run from the CLI then the request will not have a
// session.
if ($this->currentUser
->isAnonymous() && $this->requestStack
->getCurrentRequest()
->hasSession()) {
$this->requestStack
->getCurrentRequest()
->getSession()
->set('core.tempstore.shared.owner', $this->owner);
}
}
}
Classes
Name | Description |
---|---|
SharedTempStore | Stores and retrieves temporary data for a given owner. |