1 | <?php
|
---|
2 | /**
|
---|
3 | * Copyright 2018 Google Inc. All Rights Reserved.
|
---|
4 | *
|
---|
5 | * Licensed under the Apache License, Version 2.0 (the "License");
|
---|
6 | * you may not use this file except in compliance with the License.
|
---|
7 | * You may obtain a copy of the License at
|
---|
8 | *
|
---|
9 | * http://www.apache.org/licenses/LICENSE-2.0
|
---|
10 | *
|
---|
11 | * Unless required by applicable law or agreed to in writing, software
|
---|
12 | * distributed under the License is distributed on an "AS IS" BASIS,
|
---|
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
---|
14 | * See the License for the specific language governing permissions and
|
---|
15 | * limitations under the License.
|
---|
16 | */
|
---|
17 | namespace Google\Auth\Cache;
|
---|
18 |
|
---|
19 | use Psr\Cache\CacheItemInterface;
|
---|
20 | use Psr\Cache\CacheItemPoolInterface;
|
---|
21 |
|
---|
22 | /**
|
---|
23 | * SystemV shared memory based CacheItemPool implementation.
|
---|
24 | *
|
---|
25 | * This CacheItemPool implementation can be used among multiple processes, but
|
---|
26 | * it doesn't provide any locking mechanism. If multiple processes write to
|
---|
27 | * this ItemPool, you have to avoid race condition manually in your code.
|
---|
28 | */
|
---|
29 | class SysVCacheItemPool implements CacheItemPoolInterface
|
---|
30 | {
|
---|
31 | const VAR_KEY = 1;
|
---|
32 |
|
---|
33 | const DEFAULT_PROJ = 'A';
|
---|
34 |
|
---|
35 | const DEFAULT_MEMSIZE = 10000;
|
---|
36 |
|
---|
37 | const DEFAULT_PERM = 0600;
|
---|
38 |
|
---|
39 | /**
|
---|
40 | * @var int
|
---|
41 | */
|
---|
42 | private $sysvKey;
|
---|
43 |
|
---|
44 | /**
|
---|
45 | * @var CacheItemInterface[]
|
---|
46 | */
|
---|
47 | private $items;
|
---|
48 |
|
---|
49 | /**
|
---|
50 | * @var CacheItemInterface[]
|
---|
51 | */
|
---|
52 | private $deferredItems;
|
---|
53 |
|
---|
54 | /**
|
---|
55 | * @var array<mixed>
|
---|
56 | */
|
---|
57 | private $options;
|
---|
58 |
|
---|
59 | /**
|
---|
60 | * @var bool
|
---|
61 | */
|
---|
62 | private $hasLoadedItems = false;
|
---|
63 |
|
---|
64 | /**
|
---|
65 | * Create a SystemV shared memory based CacheItemPool.
|
---|
66 | *
|
---|
67 | * @param array<mixed> $options {
|
---|
68 | * [optional] Configuration options.
|
---|
69 | *
|
---|
70 | * @type int $variableKey The variable key for getting the data from the shared memory. **Defaults to** 1.
|
---|
71 | * @type string $proj The project identifier for ftok. This needs to be a one character string.
|
---|
72 | * **Defaults to** 'A'.
|
---|
73 | * @type int $memsize The memory size in bytes for shm_attach. **Defaults to** 10000.
|
---|
74 | * @type int $perm The permission for shm_attach. **Defaults to** 0600.
|
---|
75 | * }
|
---|
76 | */
|
---|
77 | public function __construct($options = [])
|
---|
78 | {
|
---|
79 | if (! extension_loaded('sysvshm')) {
|
---|
80 | throw new \RuntimeException(
|
---|
81 | 'sysvshm extension is required to use this ItemPool'
|
---|
82 | );
|
---|
83 | }
|
---|
84 | $this->options = $options + [
|
---|
85 | 'variableKey' => self::VAR_KEY,
|
---|
86 | 'proj' => self::DEFAULT_PROJ,
|
---|
87 | 'memsize' => self::DEFAULT_MEMSIZE,
|
---|
88 | 'perm' => self::DEFAULT_PERM
|
---|
89 | ];
|
---|
90 | $this->items = [];
|
---|
91 | $this->deferredItems = [];
|
---|
92 | $this->sysvKey = ftok(__FILE__, $this->options['proj']);
|
---|
93 | }
|
---|
94 |
|
---|
95 | /**
|
---|
96 | * @param mixed $key
|
---|
97 | * @return CacheItemInterface
|
---|
98 | */
|
---|
99 | public function getItem($key): CacheItemInterface
|
---|
100 | {
|
---|
101 | $this->loadItems();
|
---|
102 | return current($this->getItems([$key])); // @phpstan-ignore-line
|
---|
103 | }
|
---|
104 |
|
---|
105 | /**
|
---|
106 | * @param array<mixed> $keys
|
---|
107 | * @return iterable<CacheItemInterface>
|
---|
108 | */
|
---|
109 | public function getItems(array $keys = []): iterable
|
---|
110 | {
|
---|
111 | $this->loadItems();
|
---|
112 | $items = [];
|
---|
113 | foreach ($keys as $key) {
|
---|
114 | $items[$key] = $this->hasItem($key) ?
|
---|
115 | clone $this->items[$key] :
|
---|
116 | new TypedItem($key);
|
---|
117 | }
|
---|
118 | return $items;
|
---|
119 | }
|
---|
120 |
|
---|
121 | /**
|
---|
122 | * {@inheritdoc}
|
---|
123 | */
|
---|
124 | public function hasItem($key): bool
|
---|
125 | {
|
---|
126 | $this->loadItems();
|
---|
127 | return isset($this->items[$key]) && $this->items[$key]->isHit();
|
---|
128 | }
|
---|
129 |
|
---|
130 | /**
|
---|
131 | * {@inheritdoc}
|
---|
132 | */
|
---|
133 | public function clear(): bool
|
---|
134 | {
|
---|
135 | $this->items = [];
|
---|
136 | $this->deferredItems = [];
|
---|
137 | return $this->saveCurrentItems();
|
---|
138 | }
|
---|
139 |
|
---|
140 | /**
|
---|
141 | * {@inheritdoc}
|
---|
142 | */
|
---|
143 | public function deleteItem($key): bool
|
---|
144 | {
|
---|
145 | return $this->deleteItems([$key]);
|
---|
146 | }
|
---|
147 |
|
---|
148 | /**
|
---|
149 | * {@inheritdoc}
|
---|
150 | */
|
---|
151 | public function deleteItems(array $keys): bool
|
---|
152 | {
|
---|
153 | if (!$this->hasLoadedItems) {
|
---|
154 | $this->loadItems();
|
---|
155 | }
|
---|
156 |
|
---|
157 | foreach ($keys as $key) {
|
---|
158 | unset($this->items[$key]);
|
---|
159 | }
|
---|
160 | return $this->saveCurrentItems();
|
---|
161 | }
|
---|
162 |
|
---|
163 | /**
|
---|
164 | * {@inheritdoc}
|
---|
165 | */
|
---|
166 | public function save(CacheItemInterface $item): bool
|
---|
167 | {
|
---|
168 | if (!$this->hasLoadedItems) {
|
---|
169 | $this->loadItems();
|
---|
170 | }
|
---|
171 |
|
---|
172 | $this->items[$item->getKey()] = $item;
|
---|
173 | return $this->saveCurrentItems();
|
---|
174 | }
|
---|
175 |
|
---|
176 | /**
|
---|
177 | * {@inheritdoc}
|
---|
178 | */
|
---|
179 | public function saveDeferred(CacheItemInterface $item): bool
|
---|
180 | {
|
---|
181 | $this->deferredItems[$item->getKey()] = $item;
|
---|
182 | return true;
|
---|
183 | }
|
---|
184 |
|
---|
185 | /**
|
---|
186 | * {@inheritdoc}
|
---|
187 | */
|
---|
188 | public function commit(): bool
|
---|
189 | {
|
---|
190 | foreach ($this->deferredItems as $item) {
|
---|
191 | if ($this->save($item) === false) {
|
---|
192 | return false;
|
---|
193 | }
|
---|
194 | }
|
---|
195 | $this->deferredItems = [];
|
---|
196 | return true;
|
---|
197 | }
|
---|
198 |
|
---|
199 | /**
|
---|
200 | * Save the current items.
|
---|
201 | *
|
---|
202 | * @return bool true when success, false upon failure
|
---|
203 | */
|
---|
204 | private function saveCurrentItems()
|
---|
205 | {
|
---|
206 | $shmid = shm_attach(
|
---|
207 | $this->sysvKey,
|
---|
208 | $this->options['memsize'],
|
---|
209 | $this->options['perm']
|
---|
210 | );
|
---|
211 | if ($shmid !== false) {
|
---|
212 | $ret = shm_put_var(
|
---|
213 | $shmid,
|
---|
214 | $this->options['variableKey'],
|
---|
215 | $this->items
|
---|
216 | );
|
---|
217 | shm_detach($shmid);
|
---|
218 | return $ret;
|
---|
219 | }
|
---|
220 | return false;
|
---|
221 | }
|
---|
222 |
|
---|
223 | /**
|
---|
224 | * Load the items from the shared memory.
|
---|
225 | *
|
---|
226 | * @return bool true when success, false upon failure
|
---|
227 | */
|
---|
228 | private function loadItems()
|
---|
229 | {
|
---|
230 | $shmid = shm_attach(
|
---|
231 | $this->sysvKey,
|
---|
232 | $this->options['memsize'],
|
---|
233 | $this->options['perm']
|
---|
234 | );
|
---|
235 | if ($shmid !== false) {
|
---|
236 | $data = @shm_get_var($shmid, $this->options['variableKey']);
|
---|
237 | if (!empty($data)) {
|
---|
238 | $this->items = $data;
|
---|
239 | } else {
|
---|
240 | $this->items = [];
|
---|
241 | }
|
---|
242 | shm_detach($shmid);
|
---|
243 | $this->hasLoadedItems = true;
|
---|
244 | return true;
|
---|
245 | }
|
---|
246 | return false;
|
---|
247 | }
|
---|
248 | }
|
---|