1 | <?php |
||
2 | |||
3 | namespace Elgg\Cache; |
||
4 | |||
5 | use DateTime; |
||
6 | use Elgg\Config; |
||
7 | use ElggCache; |
||
8 | use Stash\Driver\BlackHole; |
||
9 | use Stash\Driver\Composite; |
||
10 | use Stash\Driver\Ephemeral; |
||
11 | use Stash\Driver\FileSystem; |
||
12 | use Stash\Driver\Memcache; |
||
13 | use Stash\Driver\Redis; |
||
14 | use Stash\Pool; |
||
15 | |||
16 | /** |
||
17 | * Composite cache pool |
||
18 | * |
||
19 | * @internal |
||
20 | */ |
||
21 | class CompositeCache extends ElggCache { |
||
22 | |||
23 | /** |
||
24 | * TTL of saved items (default timeout after a day to prevent anything getting too stale) |
||
25 | */ |
||
26 | protected $ttl = 86400; |
||
27 | |||
28 | /** |
||
29 | * @var Config |
||
30 | */ |
||
31 | protected $config; |
||
32 | |||
33 | /** |
||
34 | * @var int |
||
35 | */ |
||
36 | protected $flags; |
||
37 | |||
38 | /** |
||
39 | * @var Pool |
||
40 | */ |
||
41 | protected $pool; |
||
42 | |||
43 | /** |
||
44 | * @var string |
||
45 | */ |
||
46 | protected $namespace; |
||
47 | |||
48 | /** |
||
49 | * Constructor |
||
50 | * |
||
51 | * @param string $namespace Cache namespace |
||
52 | * @param Config $config Elgg config |
||
53 | * @param int $flags Start flags |
||
54 | * |
||
55 | * @throws \ConfigurationException |
||
56 | */ |
||
57 | 4421 | public function __construct($namespace, Config $config, $flags) { |
|
58 | 4421 | parent::__construct(); |
|
59 | |||
60 | 4421 | $this->namespace = $namespace; |
|
61 | 4421 | $this->config = $config; |
|
62 | 4421 | $this->flags = $flags; |
|
63 | 4421 | $this->pool = $this->createPool(); |
|
64 | 4421 | } |
|
65 | |||
66 | /** |
||
67 | * Returns cache pool |
||
68 | * @return Pool |
||
69 | */ |
||
70 | public function getPool() { |
||
71 | return $this->pool; |
||
72 | } |
||
73 | |||
74 | /** |
||
75 | * Save data in a cache. |
||
76 | * |
||
77 | * @param string $key Name |
||
78 | * @param string $data Value |
||
79 | * @param int|DateTime $expire_after Expire value after |
||
80 | * @param array $invalidation_method Stash invalidation method arguments |
||
81 | * |
||
82 | * @return bool |
||
83 | */ |
||
84 | 5358 | public function save($key, $data, $expire_after = null, $invalidation_method = null) { |
|
85 | 5358 | if (!is_string($key) && !is_int($key)) { |
|
86 | throw new \InvalidArgumentException('key must be string or integer'); |
||
87 | } |
||
88 | |||
89 | 5358 | $item = $this->pool->getItem($this->namespaceKey($key)); |
|
90 | 5358 | $item->lock(); |
|
91 | |||
92 | 5358 | if (is_array($invalidation_method)) { |
|
93 | 13 | call_user_func_array([$item, 'setInvalidationMethod'], $invalidation_method); |
|
94 | } |
||
95 | |||
96 | 5358 | $item->expiresAfter($expire_after ? : $this->ttl); |
|
97 | |||
98 | 5358 | return $item->set($data)->save(); |
|
99 | } |
||
100 | |||
101 | /** |
||
102 | * Load data from the cache using a given key. |
||
103 | * |
||
104 | * @param string $key Name |
||
105 | * |
||
106 | * @return mixed The stored data or false. |
||
107 | */ |
||
108 | 5381 | public function load($key) { |
|
109 | 5381 | if (!is_string($key) && !is_int($key)) { |
|
110 | throw new \InvalidArgumentException('key must be string or integer'); |
||
111 | } |
||
112 | |||
113 | 5381 | $item = $this->pool->getItem($this->namespaceKey($key)); |
|
114 | |||
115 | 5381 | if ($item->isMiss()) { |
|
116 | 5357 | return null; |
|
117 | } |
||
118 | |||
119 | 2377 | return $item->get(); |
|
120 | } |
||
121 | |||
122 | /** |
||
123 | * Invalidate a key |
||
124 | * |
||
125 | * @param string $key Name |
||
126 | * |
||
127 | * @return bool |
||
128 | */ |
||
129 | 1096 | public function delete($key) { |
|
130 | 1096 | if (!is_string($key) && !is_int($key)) { |
|
131 | throw new \InvalidArgumentException('key must be string or integer'); |
||
132 | } |
||
133 | |||
134 | 1096 | $this->pool->deleteItem($this->namespaceKey($key)); |
|
135 | |||
136 | 1096 | return true; |
|
137 | } |
||
138 | |||
139 | /** |
||
140 | * Clear out all the contents of the cache. |
||
141 | * |
||
142 | * @return bool |
||
143 | */ |
||
144 | 5389 | public function clear() { |
|
145 | 5389 | $this->pool->deleteItems([$this->namespaceKey('')]); |
|
146 | |||
147 | 5389 | return true; |
|
148 | } |
||
149 | |||
150 | /** |
||
151 | * Set the namespace of this cache. |
||
152 | * This is useful for cache types (like memcache or static variables) where there is one large |
||
153 | * flat area of memory shared across all instances of the cache. |
||
154 | * |
||
155 | * @param string $namespace Namespace for cache |
||
156 | * |
||
157 | * @return void |
||
158 | */ |
||
159 | public function setNamespace($namespace = "default") { |
||
160 | $this->namespace = $namespace; |
||
161 | } |
||
162 | |||
163 | /** |
||
164 | * Get the namespace currently defined. |
||
165 | * |
||
166 | * @return string |
||
167 | */ |
||
168 | 5389 | public function getNamespace() { |
|
169 | 5389 | return $this->namespace; |
|
170 | } |
||
171 | |||
172 | /** |
||
173 | * Namespace the key |
||
174 | * |
||
175 | * @param string $key Value name |
||
176 | * |
||
177 | * @return string |
||
178 | */ |
||
179 | 5389 | public function namespaceKey($key) { |
|
180 | 5389 | return "/{$this->getNamespace()}/$key"; |
|
181 | } |
||
182 | |||
183 | /** |
||
184 | * Create a new composite stash pool |
||
185 | * @return Pool |
||
186 | * @throws \ConfigurationException |
||
187 | */ |
||
188 | 4421 | protected function createPool() { |
|
189 | 4421 | $drivers = []; |
|
190 | 4421 | $drivers[] = $this->buildRedisDriver(); |
|
191 | 4421 | $drivers[] = $this->buildMemcachedDriver(); |
|
192 | 4421 | $drivers[] = $this->buildFileSystemDriver(); |
|
193 | 4421 | $drivers[] = $this->buildEphemeralDriver(); |
|
194 | 4421 | $drivers[] = $this->buildBlackHoleDriver(); |
|
195 | |||
196 | 4421 | $drivers = array_filter($drivers); |
|
197 | |||
198 | 4421 | if (empty($drivers)) { |
|
199 | throw new \ConfigurationException("Unable to initialize composite cache without drivers"); |
||
200 | } |
||
201 | |||
202 | 4421 | if (count($drivers) > 1) { |
|
203 | 4421 | $driver = new Composite([ |
|
204 | 4421 | 'drivers' => $drivers, |
|
205 | ]); |
||
206 | } else { |
||
207 | 4417 | $driver = array_shift($drivers); |
|
208 | } |
||
209 | |||
210 | 4421 | return new Pool($driver); |
|
211 | } |
||
212 | |||
213 | /** |
||
214 | * Builds Redis driver |
||
215 | * @return null|Redis |
||
216 | */ |
||
217 | 4421 | protected function buildRedisDriver() { |
|
218 | 4421 | if (!($this->flags & ELGG_CACHE_PERSISTENT)) { |
|
219 | 4417 | return null; |
|
220 | } |
||
221 | |||
222 | 4421 | if (!$this->config->redis || !$this->config->redis_servers) { |
|
0 ignored issues
–
show
|
|||
223 | 4421 | return null; |
|
224 | } |
||
225 | |||
226 | return new Redis([ |
||
227 | 'servers' => $this->config->redis_servers, |
||
228 | ]); |
||
229 | } |
||
230 | |||
231 | /** |
||
232 | * Builds Memcached driver |
||
233 | * @return null|Memcache |
||
234 | */ |
||
235 | 4421 | protected function buildMemcachedDriver() { |
|
236 | 4421 | if (!($this->flags & ELGG_CACHE_PERSISTENT)) { |
|
237 | 4417 | return null; |
|
238 | } |
||
239 | |||
240 | 4421 | $has_class = class_exists('Memcache') || class_exists('Memcached'); |
|
241 | 4421 | if (!$has_class) { |
|
242 | return null; |
||
243 | } |
||
244 | |||
245 | 4421 | if (!$this->config->memcache || !$this->config->memcache_servers) { |
|
0 ignored issues
–
show
The expression
$this->config->memcache_servers of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using
Loading history...
|
|||
246 | 6 | return null; |
|
247 | } |
||
248 | |||
249 | 4421 | return new Memcache([ |
|
250 | 4421 | 'servers' => $this->config->memcache_servers, |
|
251 | 'options' => [ |
||
252 | 4421 | 'prefix_key' => $this->config->memcache_namespace_prefix, |
|
253 | ] |
||
254 | ]); |
||
255 | } |
||
256 | |||
257 | /** |
||
258 | * Builds file system driver |
||
259 | * @return null|FileSystem |
||
260 | */ |
||
261 | 4421 | protected function buildFileSystemDriver() { |
|
262 | 4421 | if (!($this->flags & ELGG_CACHE_FILESYSTEM)) { |
|
263 | 4420 | return null; |
|
264 | } |
||
265 | |||
266 | 4418 | $path = $this->config->cacheroot ? : $this->config->dataroot; |
|
267 | 4418 | if (!$path) { |
|
268 | 1 | return null; |
|
269 | } |
||
270 | |||
271 | 4418 | return new FileSystem([ |
|
272 | 4418 | 'path' => $path, |
|
273 | ]); |
||
274 | } |
||
275 | |||
276 | /** |
||
277 | * Builds in-memory driver |
||
278 | * @return Ephemeral |
||
279 | */ |
||
280 | 4421 | protected function buildEphemeralDriver() { |
|
281 | 4421 | if (!($this->flags & ELGG_CACHE_RUNTIME)) { |
|
282 | 20 | return null; |
|
283 | } |
||
284 | |||
285 | 4421 | return new Ephemeral(); |
|
286 | } |
||
287 | |||
288 | /** |
||
289 | * Builds null cache driver |
||
290 | * @return BlackHole |
||
291 | */ |
||
292 | 4421 | protected function buildBlackHoleDriver() { |
|
293 | 4421 | if (!($this->flags & ELGG_CACHE_BLACK_HOLE)) { |
|
294 | 4421 | return null; |
|
295 | } |
||
296 | |||
297 | return new BlackHole(); |
||
298 | } |
||
299 | } |
||
300 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.