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 Drupal\Core\Session\AccountProxyInterface;
|
Chris@0
|
8 use Symfony\Component\HttpFoundation\RequestStack;
|
Chris@0
|
9
|
Chris@0
|
10 /**
|
Chris@0
|
11 * Stores and retrieves temporary data for a given owner.
|
Chris@0
|
12 *
|
Chris@0
|
13 * A PrivateTempStore can be used to make temporary, non-cache data available
|
Chris@0
|
14 * across requests. The data for the PrivateTempStore is stored in one
|
Chris@0
|
15 * key/value collection. PrivateTempStore data expires automatically after a
|
Chris@0
|
16 * given timeframe.
|
Chris@0
|
17 *
|
Chris@0
|
18 * The PrivateTempStore is different from a cache, because the data in it is not
|
Chris@0
|
19 * yet saved permanently and so it cannot be rebuilt. Typically, the
|
Chris@0
|
20 * PrivateTempStore might be used to store work in progress that is later saved
|
Chris@0
|
21 * permanently elsewhere, e.g. autosave data, multistep forms, or in-progress
|
Chris@0
|
22 * changes to complex configuration that are not ready to be saved.
|
Chris@0
|
23 *
|
Chris@0
|
24 * The PrivateTempStore differs from the SharedTempStore in that all keys are
|
Chris@0
|
25 * ensured to be unique for a particular user and users can never share data. If
|
Chris@0
|
26 * you want to be able to share data between users or use it for locking, use
|
Chris@0
|
27 * \Drupal\user\SharedTempStore.
|
Chris@0
|
28 */
|
Chris@0
|
29 class PrivateTempStore {
|
Chris@0
|
30
|
Chris@0
|
31 /**
|
Chris@0
|
32 * The key/value storage object used for this data.
|
Chris@0
|
33 *
|
Chris@0
|
34 * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
|
Chris@0
|
35 */
|
Chris@0
|
36 protected $storage;
|
Chris@0
|
37
|
Chris@0
|
38 /**
|
Chris@0
|
39 * The lock object used for this data.
|
Chris@0
|
40 *
|
Chris@0
|
41 * @var \Drupal\Core\Lock\LockBackendInterface
|
Chris@0
|
42 */
|
Chris@0
|
43 protected $lockBackend;
|
Chris@0
|
44
|
Chris@0
|
45 /**
|
Chris@0
|
46 * The current user.
|
Chris@0
|
47 *
|
Chris@0
|
48 * @var \Drupal\Core\Session\AccountProxyInterface
|
Chris@0
|
49 */
|
Chris@0
|
50 protected $currentUser;
|
Chris@0
|
51
|
Chris@0
|
52 /**
|
Chris@0
|
53 * The request stack.
|
Chris@0
|
54 *
|
Chris@0
|
55 * @var \Symfony\Component\HttpFoundation\RequestStack
|
Chris@0
|
56 */
|
Chris@0
|
57 protected $requestStack;
|
Chris@0
|
58
|
Chris@0
|
59 /**
|
Chris@0
|
60 * The time to live for items in seconds.
|
Chris@0
|
61 *
|
Chris@0
|
62 * By default, data is stored for one week (604800 seconds) before expiring.
|
Chris@0
|
63 *
|
Chris@0
|
64 * @var int
|
Chris@0
|
65 */
|
Chris@0
|
66 protected $expire;
|
Chris@0
|
67
|
Chris@0
|
68 /**
|
Chris@0
|
69 * Constructs a new object for accessing data from a key/value store.
|
Chris@0
|
70 *
|
Chris@0
|
71 * @param KeyValueStoreExpirableInterface $storage
|
Chris@0
|
72 * The key/value storage object used for this data. Each storage object
|
Chris@0
|
73 * represents a particular collection of data and will contain any number
|
Chris@0
|
74 * of key/value pairs.
|
Chris@0
|
75 * @param \Drupal\Core\Lock\LockBackendInterface $lock_backend
|
Chris@0
|
76 * The lock object used for this data.
|
Chris@0
|
77 * @param \Drupal\Core\Session\AccountProxyInterface $current_user
|
Chris@0
|
78 * The current user account.
|
Chris@0
|
79 * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
|
Chris@0
|
80 * The request stack.
|
Chris@0
|
81 * @param int $expire
|
Chris@0
|
82 * The time to live for items, in seconds.
|
Chris@0
|
83 */
|
Chris@0
|
84 public function __construct(KeyValueStoreExpirableInterface $storage, LockBackendInterface $lock_backend, AccountProxyInterface $current_user, RequestStack $request_stack, $expire = 604800) {
|
Chris@0
|
85 $this->storage = $storage;
|
Chris@0
|
86 $this->lockBackend = $lock_backend;
|
Chris@0
|
87 $this->currentUser = $current_user;
|
Chris@0
|
88 $this->requestStack = $request_stack;
|
Chris@0
|
89 $this->expire = $expire;
|
Chris@0
|
90 }
|
Chris@0
|
91
|
Chris@0
|
92 /**
|
Chris@0
|
93 * Retrieves a value from this PrivateTempStore for a given key.
|
Chris@0
|
94 *
|
Chris@0
|
95 * @param string $key
|
Chris@0
|
96 * The key of the data to retrieve.
|
Chris@0
|
97 *
|
Chris@0
|
98 * @return mixed
|
Chris@0
|
99 * The data associated with the key, or NULL if the key does not exist.
|
Chris@0
|
100 */
|
Chris@0
|
101 public function get($key) {
|
Chris@0
|
102 $key = $this->createkey($key);
|
Chris@0
|
103 if (($object = $this->storage->get($key)) && ($object->owner == $this->getOwner())) {
|
Chris@0
|
104 return $object->data;
|
Chris@0
|
105 }
|
Chris@0
|
106 }
|
Chris@0
|
107
|
Chris@0
|
108 /**
|
Chris@0
|
109 * Stores a particular key/value pair in this PrivateTempStore.
|
Chris@0
|
110 *
|
Chris@0
|
111 * @param string $key
|
Chris@0
|
112 * The key of the data to store.
|
Chris@0
|
113 * @param mixed $value
|
Chris@0
|
114 * The data to store.
|
Chris@0
|
115 *
|
Chris@0
|
116 * @throws \Drupal\user\TempStoreException
|
Chris@0
|
117 * Thrown when a lock for the backend storage could not be acquired.
|
Chris@0
|
118 */
|
Chris@0
|
119 public function set($key, $value) {
|
Chris@0
|
120 $key = $this->createkey($key);
|
Chris@0
|
121 if (!$this->lockBackend->acquire($key)) {
|
Chris@0
|
122 $this->lockBackend->wait($key);
|
Chris@0
|
123 if (!$this->lockBackend->acquire($key)) {
|
Chris@0
|
124 throw new TempStoreException("Couldn't acquire lock to update item '$key' in '{$this->storage->getCollectionName()}' temporary storage.");
|
Chris@0
|
125 }
|
Chris@0
|
126 }
|
Chris@0
|
127
|
Chris@0
|
128 $value = (object) [
|
Chris@0
|
129 'owner' => $this->getOwner(),
|
Chris@0
|
130 'data' => $value,
|
Chris@0
|
131 'updated' => (int) $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME'),
|
Chris@0
|
132 ];
|
Chris@0
|
133 $this->storage->setWithExpire($key, $value, $this->expire);
|
Chris@0
|
134 $this->lockBackend->release($key);
|
Chris@0
|
135 }
|
Chris@0
|
136
|
Chris@0
|
137 /**
|
Chris@0
|
138 * Returns the metadata associated with a particular key/value pair.
|
Chris@0
|
139 *
|
Chris@0
|
140 * @param string $key
|
Chris@0
|
141 * The key of the data to store.
|
Chris@0
|
142 *
|
Chris@0
|
143 * @return mixed
|
Chris@0
|
144 * An object with the owner and updated time if the key has a value, or
|
Chris@0
|
145 * NULL otherwise.
|
Chris@0
|
146 */
|
Chris@0
|
147 public function getMetadata($key) {
|
Chris@0
|
148 $key = $this->createkey($key);
|
Chris@0
|
149 // Fetch the key/value pair and its metadata.
|
Chris@0
|
150 $object = $this->storage->get($key);
|
Chris@0
|
151 if ($object) {
|
Chris@0
|
152 // Don't keep the data itself in memory.
|
Chris@0
|
153 unset($object->data);
|
Chris@0
|
154 return $object;
|
Chris@0
|
155 }
|
Chris@0
|
156 }
|
Chris@0
|
157
|
Chris@0
|
158 /**
|
Chris@0
|
159 * Deletes data from the store for a given key and releases the lock on it.
|
Chris@0
|
160 *
|
Chris@0
|
161 * @param string $key
|
Chris@0
|
162 * The key of the data to delete.
|
Chris@0
|
163 *
|
Chris@0
|
164 * @return bool
|
Chris@0
|
165 * TRUE if the object was deleted or does not exist, FALSE if it exists but
|
Chris@0
|
166 * is not owned by $this->owner.
|
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 delete($key) {
|
Chris@0
|
172 $key = $this->createkey($key);
|
Chris@0
|
173 if (!$object = $this->storage->get($key)) {
|
Chris@0
|
174 return TRUE;
|
Chris@0
|
175 }
|
Chris@0
|
176 elseif ($object->owner != $this->getOwner()) {
|
Chris@0
|
177 return FALSE;
|
Chris@0
|
178 }
|
Chris@0
|
179 if (!$this->lockBackend->acquire($key)) {
|
Chris@0
|
180 $this->lockBackend->wait($key);
|
Chris@0
|
181 if (!$this->lockBackend->acquire($key)) {
|
Chris@0
|
182 throw new TempStoreException("Couldn't acquire lock to delete item '$key' from '{$this->storage->getCollectionName()}' temporary storage.");
|
Chris@0
|
183 }
|
Chris@0
|
184 }
|
Chris@0
|
185 $this->storage->delete($key);
|
Chris@0
|
186 $this->lockBackend->release($key);
|
Chris@0
|
187 return TRUE;
|
Chris@0
|
188 }
|
Chris@0
|
189
|
Chris@0
|
190 /**
|
Chris@0
|
191 * Ensures that the key is unique for a user.
|
Chris@0
|
192 *
|
Chris@0
|
193 * @param string $key
|
Chris@0
|
194 * The key.
|
Chris@0
|
195 *
|
Chris@0
|
196 * @return string
|
Chris@0
|
197 * The unique key for the user.
|
Chris@0
|
198 */
|
Chris@0
|
199 protected function createkey($key) {
|
Chris@0
|
200 return $this->getOwner() . ':' . $key;
|
Chris@0
|
201 }
|
Chris@0
|
202
|
Chris@0
|
203 /**
|
Chris@0
|
204 * Gets the current owner based on the current user or the session ID.
|
Chris@0
|
205 *
|
Chris@0
|
206 * @return string
|
Chris@0
|
207 * The owner.
|
Chris@0
|
208 */
|
Chris@0
|
209 protected function getOwner() {
|
Chris@0
|
210 return $this->currentUser->id() ?: $this->requestStack->getCurrentRequest()->getSession()->getId();
|
Chris@0
|
211 }
|
Chris@0
|
212
|
Chris@0
|
213 }
|