Elgg  Version 3.0
CompositeCache.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Cache;
4 
5 use DateTime;
6 use Elgg\Config;
7 use ElggCache;
15 use Stash\Pool;
16 
22 class CompositeCache extends ElggCache {
23 
27  protected $ttl = 86400;
28 
32  protected $config;
33 
37  protected $flags;
38 
42  protected $pool;
43 
47  protected $namespace;
48 
59  parent::__construct();
60 
61  $this->namespace = $namespace;
62  $this->config = $config;
63  $this->flags = $flags;
64  $this->pool = $this->createPool();
65  }
66 
71  public function getPool() {
72  return $this->pool;
73  }
74 
84  public function save($key, $data, $ttl = null) {
85  if ($this->disabled) {
86  return false;
87  }
88 
89  if (!is_string($key) && !is_int($key)) {
90  throw new \InvalidArgumentException('key must be string or integer');
91  }
92 
93  $item = $this->pool->getItem($this->namespaceKey($key));
94  $item->lock();
95 
96  $item->setTTL($ttl ? : $this->ttl);
97 
98  return $item->set($data)->save();
99  }
100 
109  public function load($key, $invalidation_method = null) {
110  if ($this->disabled) {
111  return null;
112  }
113 
114  if (!is_string($key) && !is_int($key)) {
115  throw new \InvalidArgumentException('key must be string or integer');
116  }
117 
118  $item = $this->pool->getItem($this->namespaceKey($key));
119 
120  if (is_array($invalidation_method)) {
121  call_user_func_array([$item, 'setInvalidationMethod'], $invalidation_method);
122  }
123 
124  try {
125  if ($item->isMiss()) {
126  return null;
127  }
128 
129  return $item->get();
130  } catch (\Error $e) {
131  // catching parsing errors in file driver, because of potential race conditions during write
132  // this will cause corrupted data in the file and will crash the site when reading the file
133  elgg_log(__METHOD__ . " failed for key: {$this->getNamespace()}/{$key} with error: {$e->getMessage()}", 'ERROR');
134 
135  // remove the item from the cache so it can try to generate this item again
136  $this->delete($key);
137  }
138 
139  return null;
140  }
141 
149  public function delete($key) {
150  if ($this->disabled) {
151  return false;
152  }
153 
154  if (!is_string($key) && !is_int($key)) {
155  throw new \InvalidArgumentException('key must be string or integer');
156  }
157 
158  $this->pool->deleteItem($this->namespaceKey($key));
159 
160  return true;
161  }
162 
168  public function clear() {
169  $this->pool->deleteItems([$this->namespaceKey('')]);
170 
171  return true;
172  }
173 
183  public function setNamespace($namespace = "default") {
184  $this->namespace = $namespace;
185  }
186 
192  public function getNamespace() {
193  return $this->namespace;
194  }
195 
203  public function namespaceKey($key) {
204  return "/{$this->getNamespace()}/$key";
205  }
206 
212  protected function createPool() {
213  $drivers = [];
214  $drivers[] = $this->buildEphemeralDriver();
215  $drivers[] = $this->buildApcDriver();
216  $drivers[] = $this->buildRedisDriver();
217  $drivers[] = $this->buildMemcachedDriver();
218  $drivers[] = $this->buildFileSystemDriver();
219  $drivers[] = $this->buildBlackHoleDriver();
220  $drivers = array_filter($drivers);
221 
222  if (empty($drivers)) {
223  throw new \ConfigurationException("Unable to initialize composite cache without drivers");
224  }
225 
226  if (count($drivers) > 1) {
227  $driver = new Composite([
228  'drivers' => $drivers,
229  ]);
230  } else {
231  $driver = array_shift($drivers);
232  }
233 
234  return new Pool($driver);
235  }
236 
241  protected function buildApcDriver() {
242  if (!($this->flags & ELGG_CACHE_APC)) {
243  return null;
244  }
245 
246  if (!extension_loaded('apc') || !ini_get('apc.enabled')) {
247  return null;
248  }
249 
250  return new Apc();
251  }
252 
257  protected function buildRedisDriver() {
258  if (!($this->flags & ELGG_CACHE_PERSISTENT)) {
259  return null;
260  }
261 
262  if (!$this->config->redis || empty($this->config->redis_servers)) {
263  return null;
264  }
265 
266  return new Redis([
267  'servers' => $this->config->redis_servers,
268  ]);
269  }
270 
275  protected function buildMemcachedDriver() {
276  if (!($this->flags & ELGG_CACHE_PERSISTENT)) {
277  return null;
278  }
279 
280  if (!$this->config->memcache || empty($this->config->memcache_servers)) {
281  return null;
282  }
283 
284  $has_class = class_exists('Memcache') || class_exists('Memcached');
285  if (!$has_class) {
286  return null;
287  }
288 
289  return new Memcache([
290  'servers' => $this->config->memcache_servers,
291  'options' => [
292  'prefix_key' => $this->config->memcache_namespace_prefix,
293  ]
294  ]);
295  }
296 
301  protected function buildFileSystemDriver() {
302  if (!($this->flags & ELGG_CACHE_FILESYSTEM)) {
303  return null;
304  }
305 
306  $path = $this->config->cacheroot ? : $this->config->dataroot;
307  if (!$path) {
308  return null;
309  }
310 
311  return new FileSystem([
312  'path' => $path,
313  ]);
314  }
315 
320  protected function buildEphemeralDriver() {
321  if (!($this->flags & ELGG_CACHE_RUNTIME)) {
322  return null;
323  }
324 
325  return new Ephemeral();
326  }
327 
332  protected function buildBlackHoleDriver() {
333  if (!($this->flags & ELGG_CACHE_BLACK_HOLE)) {
334  return null;
335  }
336 
337  return new BlackHole();
338  }
339 }
$ttl
TTL of saved items (default timeout after a day to prevent anything getting too stale) ...
load($key, $invalidation_method=null)
Load data from the cache using a given key.
if(!$items) $item
Definition: delete.php:13
getNamespace()
Get the namespace currently defined.
getPool()
Returns cache pool.
$path
Definition: details.php:89
if(elgg_trigger_plugin_hook('usersettings:save', 'user', $hooks_params, true)) foreach($request->validation() ->all() as $item) $data
Definition: save.php:57
const ELGG_CACHE_BLACK_HOLE
Cache init values.
Definition: constants.php:147
buildEphemeralDriver()
Builds in-memory driver.
const ELGG_CACHE_APC
Definition: constants.php:151
Composite cache pool.
buildMemcachedDriver()
Builds Memcached driver.
namespaceKey($key)
Namespace the key.
const ELGG_CACHE_RUNTIME
Definition: constants.php:148
clear()
Clear out all the contents of the cache.
buildApcDriver()
Builds APC driver.
elgg_log($message, $level=\Psr\Log\LogLevel::NOTICE)
Log a message.
Definition: elgglib.php:786
save($key, $data, $ttl=null)
Save data in a cache.
if($container instanceof ElggGroup &&$container->guid!=elgg_get_page_owner_guid()) $key
Definition: summary.php:55
createPool()
Create a new composite stash pool.
const ELGG_CACHE_PERSISTENT
Definition: constants.php:150
const ELGG_CACHE_FILESYSTEM
Definition: constants.php:149
buildRedisDriver()
Builds Redis driver.
setNamespace($namespace="default")
Set the namespace of this cache.
buildBlackHoleDriver()
Builds null cache driver.
buildFileSystemDriver()
Builds file system driver.
__construct($namespace, Config $config, $flags)
Constructor.