annotate core/modules/user/src/SharedTempStore.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 7a779792577d
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\user;
Chris@0 4
Chris@0 5 use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
Chris@0 6 use Drupal\Core\Lock\LockBackendInterface;
Chris@0 7 use Symfony\Component\HttpFoundation\RequestStack;
Chris@0 8
Chris@0 9 /**
Chris@0 10 * Stores and retrieves temporary data for a given owner.
Chris@0 11 *
Chris@0 12 * A SharedTempStore can be used to make temporary, non-cache data available
Chris@0 13 * across requests. The data for the SharedTempStore is stored in one key/value
Chris@0 14 * collection. SharedTempStore data expires automatically after a given
Chris@0 15 * timeframe.
Chris@0 16 *
Chris@0 17 * The SharedTempStore is different from a cache, because the data in it is not
Chris@0 18 * yet saved permanently and so it cannot be rebuilt. Typically, the
Chris@0 19 * SharedTempStore might be used to store work in progress that is later saved
Chris@0 20 * permanently elsewhere, e.g. autosave data, multistep forms, or in-progress
Chris@0 21 * changes to complex configuration that are not ready to be saved.
Chris@0 22 *
Chris@0 23 * Each SharedTempStore belongs to a particular owner (e.g. a user, session, or
Chris@0 24 * process). Multiple owners may use the same key/value collection, and the
Chris@0 25 * owner is stored along with the key/value pair.
Chris@0 26 *
Chris@0 27 * Every key is unique within the collection, so the SharedTempStore can check
Chris@0 28 * whether a particular key is already set by a different owner. This is
Chris@0 29 * useful for informing one owner that the data is already in use by another;
Chris@0 30 * for example, to let one user know that another user is in the process of
Chris@0 31 * editing certain data, or even to restrict other users from editing it at
Chris@0 32 * the same time. It is the responsibility of the implementation to decide
Chris@0 33 * when and whether one owner can use or update another owner's data.
Chris@0 34 *
Chris@0 35 * If you want to be able to ensure that the data belongs to the current user,
Chris@0 36 * use \Drupal\user\PrivateTempStore.
Chris@0 37 */
Chris@0 38 class SharedTempStore {
Chris@0 39
Chris@0 40 /**
Chris@0 41 * The key/value storage object used for this data.
Chris@0 42 *
Chris@0 43 * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
Chris@0 44 */
Chris@0 45 protected $storage;
Chris@0 46
Chris@0 47 /**
Chris@0 48 * The lock object used for this data.
Chris@0 49 *
Chris@0 50 * @var \Drupal\Core\Lock\LockBackendInterface
Chris@0 51 */
Chris@0 52 protected $lockBackend;
Chris@0 53
Chris@0 54 /**
Chris@0 55 * The request stack.
Chris@0 56 *
Chris@0 57 * @var \Symfony\Component\HttpFoundation\RequestStack
Chris@0 58 */
Chris@0 59 protected $requestStack;
Chris@0 60
Chris@0 61 /**
Chris@0 62 * The owner key to store along with the data (e.g. a user or session ID).
Chris@0 63 *
Chris@0 64 * @var mixed
Chris@0 65 */
Chris@0 66 protected $owner;
Chris@0 67
Chris@0 68 /**
Chris@0 69 * The time to live for items in seconds.
Chris@0 70 *
Chris@0 71 * By default, data is stored for one week (604800 seconds) before expiring.
Chris@0 72 *
Chris@0 73 * @var int
Chris@0 74 */
Chris@0 75 protected $expire;
Chris@0 76
Chris@0 77 /**
Chris@0 78 * Constructs a new object for accessing data from a key/value store.
Chris@0 79 *
Chris@0 80 * @param KeyValueStoreExpirableInterface $storage
Chris@0 81 * The key/value storage object used for this data. Each storage object
Chris@0 82 * represents a particular collection of data and will contain any number
Chris@0 83 * of key/value pairs.
Chris@0 84 * @param \Drupal\Core\Lock\LockBackendInterface $lock_backend
Chris@0 85 * The lock object used for this data.
Chris@0 86 * @param mixed $owner
Chris@0 87 * The owner key to store along with the data (e.g. a user or session ID).
Chris@0 88 * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
Chris@0 89 * The request stack.
Chris@0 90 * @param int $expire
Chris@0 91 * The time to live for items, in seconds.
Chris@0 92 */
Chris@0 93 public function __construct(KeyValueStoreExpirableInterface $storage, LockBackendInterface $lock_backend, $owner, RequestStack $request_stack, $expire = 604800) {
Chris@0 94 $this->storage = $storage;
Chris@0 95 $this->lockBackend = $lock_backend;
Chris@0 96 $this->owner = $owner;
Chris@0 97 $this->requestStack = $request_stack;
Chris@0 98 $this->expire = $expire;
Chris@0 99 }
Chris@0 100
Chris@0 101 /**
Chris@0 102 * Retrieves a value from this SharedTempStore for a given key.
Chris@0 103 *
Chris@0 104 * @param string $key
Chris@0 105 * The key of the data to retrieve.
Chris@0 106 *
Chris@0 107 * @return mixed
Chris@0 108 * The data associated with the key, or NULL if the key does not exist.
Chris@0 109 */
Chris@0 110 public function get($key) {
Chris@0 111 if ($object = $this->storage->get($key)) {
Chris@0 112 return $object->data;
Chris@0 113 }
Chris@0 114 }
Chris@0 115
Chris@0 116 /**
Chris@0 117 * Retrieves a value from this SharedTempStore for a given key.
Chris@0 118 *
Chris@0 119 * Only returns the value if the value is owned by $this->owner.
Chris@0 120 *
Chris@0 121 * @param string $key
Chris@0 122 * The key of the data to retrieve.
Chris@0 123 *
Chris@0 124 * @return mixed
Chris@0 125 * The data associated with the key, or NULL if the key does not exist.
Chris@0 126 */
Chris@0 127 public function getIfOwner($key) {
Chris@0 128 if (($object = $this->storage->get($key)) && ($object->owner == $this->owner)) {
Chris@0 129 return $object->data;
Chris@0 130 }
Chris@0 131 }
Chris@0 132
Chris@0 133 /**
Chris@0 134 * Stores a particular key/value pair only if the key doesn't already exist.
Chris@0 135 *
Chris@0 136 * @param string $key
Chris@0 137 * The key of the data to check and store.
Chris@0 138 * @param mixed $value
Chris@0 139 * The data to store.
Chris@0 140 *
Chris@0 141 * @return bool
Chris@0 142 * TRUE if the data was set, or FALSE if it already existed.
Chris@0 143 */
Chris@0 144 public function setIfNotExists($key, $value) {
Chris@0 145 $value = (object) [
Chris@0 146 'owner' => $this->owner,
Chris@0 147 'data' => $value,
Chris@0 148 'updated' => (int) $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME'),
Chris@0 149 ];
Chris@0 150 return $this->storage->setWithExpireIfNotExists($key, $value, $this->expire);
Chris@0 151 }
Chris@0 152
Chris@0 153 /**
Chris@0 154 * Stores a particular key/value pair in this SharedTempStore.
Chris@0 155 *
Chris@0 156 * Only stores the given key/value pair if it does not exist yet or is owned
Chris@0 157 * by $this->owner.
Chris@0 158 *
Chris@0 159 * @param string $key
Chris@0 160 * The key of the data to store.
Chris@0 161 * @param mixed $value
Chris@0 162 * The data to store.
Chris@0 163 *
Chris@0 164 * @return bool
Chris@0 165 * TRUE if the data was set, or FALSE if it already exists and is not owned
Chris@0 166 * by $this->user.
Chris@0 167 *
Chris@0 168 * @throws \Drupal\user\TempStoreException
Chris@0 169 * Thrown when a lock for the backend storage could not be acquired.
Chris@0 170 */
Chris@0 171 public function setIfOwner($key, $value) {
Chris@0 172 if ($this->setIfNotExists($key, $value)) {
Chris@0 173 return TRUE;
Chris@0 174 }
Chris@0 175
Chris@0 176 if (($object = $this->storage->get($key)) && ($object->owner == $this->owner)) {
Chris@0 177 $this->set($key, $value);
Chris@0 178 return TRUE;
Chris@0 179 }
Chris@0 180
Chris@0 181 return FALSE;
Chris@0 182 }
Chris@0 183
Chris@0 184 /**
Chris@0 185 * Stores a particular key/value pair in this SharedTempStore.
Chris@0 186 *
Chris@0 187 * @param string $key
Chris@0 188 * The key of the data to store.
Chris@0 189 * @param mixed $value
Chris@0 190 * The data to store.
Chris@0 191 *
Chris@0 192 * @throws \Drupal\user\TempStoreException
Chris@0 193 * Thrown when a lock for the backend storage could not be acquired.
Chris@0 194 */
Chris@0 195 public function set($key, $value) {
Chris@0 196 if (!$this->lockBackend->acquire($key)) {
Chris@0 197 $this->lockBackend->wait($key);
Chris@0 198 if (!$this->lockBackend->acquire($key)) {
Chris@0 199 throw new TempStoreException("Couldn't acquire lock to update item '$key' in '{$this->storage->getCollectionName()}' temporary storage.");
Chris@0 200 }
Chris@0 201 }
Chris@0 202
Chris@0 203 $value = (object) [
Chris@0 204 'owner' => $this->owner,
Chris@0 205 'data' => $value,
Chris@0 206 'updated' => (int) $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME'),
Chris@0 207 ];
Chris@0 208 $this->storage->setWithExpire($key, $value, $this->expire);
Chris@0 209 $this->lockBackend->release($key);
Chris@0 210 }
Chris@0 211
Chris@0 212 /**
Chris@0 213 * Returns the metadata associated with a particular key/value pair.
Chris@0 214 *
Chris@0 215 * @param string $key
Chris@0 216 * The key of the data to store.
Chris@0 217 *
Chris@0 218 * @return mixed
Chris@0 219 * An object with the owner and updated time if the key has a value, or
Chris@0 220 * NULL otherwise.
Chris@0 221 */
Chris@0 222 public function getMetadata($key) {
Chris@0 223 // Fetch the key/value pair and its metadata.
Chris@0 224 $object = $this->storage->get($key);
Chris@0 225 if ($object) {
Chris@0 226 // Don't keep the data itself in memory.
Chris@0 227 unset($object->data);
Chris@0 228 return $object;
Chris@0 229 }
Chris@0 230 }
Chris@0 231
Chris@0 232 /**
Chris@0 233 * Deletes data from the store for a given key and releases the lock on it.
Chris@0 234 *
Chris@0 235 * @param string $key
Chris@0 236 * The key of the data to delete.
Chris@0 237 *
Chris@0 238 * @throws \Drupal\user\TempStoreException
Chris@0 239 * Thrown when a lock for the backend storage could not be acquired.
Chris@0 240 */
Chris@0 241 public function delete($key) {
Chris@0 242 if (!$this->lockBackend->acquire($key)) {
Chris@0 243 $this->lockBackend->wait($key);
Chris@0 244 if (!$this->lockBackend->acquire($key)) {
Chris@0 245 throw new TempStoreException("Couldn't acquire lock to delete item '$key' from {$this->storage->getCollectionName()} temporary storage.");
Chris@0 246 }
Chris@0 247 }
Chris@0 248 $this->storage->delete($key);
Chris@0 249 $this->lockBackend->release($key);
Chris@0 250 }
Chris@0 251
Chris@0 252 /**
Chris@0 253 * Deletes data from the store for a given key and releases the lock on it.
Chris@0 254 *
Chris@0 255 * Only delete the given key if it is owned by $this->owner.
Chris@0 256 *
Chris@0 257 * @param string $key
Chris@0 258 * The key of the data to delete.
Chris@0 259 *
Chris@0 260 * @return bool
Chris@0 261 * TRUE if the object was deleted or does not exist, FALSE if it exists but
Chris@0 262 * is not owned by $this->owner.
Chris@0 263 *
Chris@0 264 * @throws \Drupal\user\TempStoreException
Chris@0 265 * Thrown when a lock for the backend storage could not be acquired.
Chris@0 266 */
Chris@0 267 public function deleteIfOwner($key) {
Chris@0 268 if (!$object = $this->storage->get($key)) {
Chris@0 269 return TRUE;
Chris@0 270 }
Chris@0 271 elseif ($object->owner == $this->owner) {
Chris@0 272 $this->delete($key);
Chris@0 273 return TRUE;
Chris@0 274 }
Chris@0 275
Chris@0 276 return FALSE;
Chris@0 277 }
Chris@0 278
Chris@0 279 }