*/ class WordpressCache { /** * Prefix used for anything cached using the Wordpress API so it doesn't conflict * with our normal caching. */ const PREFIX = 'wp'; /** * The amount of times the cache data was already stored in the cache. * * @var int */ protected $cacheHits = 0; /** * Track how may requests were not cached. * * @var int */ protected $cacheMisses = 0; /** * Prefix to use for non-global cache items. * * @var string */ protected $blogPrefix = ''; /** * List of global groups. * * @var array */ protected $globalGroups = []; /** * Cache items to not persist in our cache. * * @var array */ protected $nonPersistentGroups = []; /** * Datastore for non-persistent items. * * @var array */ protected $nonPersistentStore = []; /** * WordpressCache constructor. */ public function __construct() { $this->blogPrefix = is_multisite() ? get_current_blog_id() : ''; } /** * Switches the internal blog ID used for non-global caching. * * @param int $blog blog to switch to */ public function switchBlog($blog) { $this->blogPrefix = is_multisite() ? $blog : ''; } /** * Adds a value to cache. * * If the specified key already exists, the value is not stored and the function * returns false. * * @param string $key The key under which to store the value. * @param mixed $value The value to store. * @param string $group The group value appended to the $key. * @param int $expiration The expiration time, defaults to 0. * * @return bool Returns TRUE on success or FALSE on failure. */ public function add($key, $value, $group = 'default', $expiration = 0) { if (wp_suspend_cache_addition()) { return false; } if (in_array($group, $this->nonPersistentGroups, true)) { if (!isset($this->nonPersistentStore[$group])) { $this->nonPersistentStore[$group] = []; } elseif (isset($this->nonPersistentStore[$group][$key])) { return false; } $this->nonPersistentStore[$group][$key] = $value; return true; } if (static::getCache($group)->has($this->buildKey($group, $key))) { return false; } if ($expiration === 0) { static::getCache($group)->forever($this->buildKey($group, $key), $value); return true; } return static::getCache($group)->add($this->buildKey($group, $key), $value, $expiration / 60); } /** * Replace a value in the cache. * * If the specified key doesn't exist, the value is not stored and the function * returns false. * * @param string $key The key under which to store the value. * @param mixed $value The value to store. * @param string $group The group value appended to the $key. * @param int $expiration The expiration time, defaults to 0. * * @return bool Returns TRUE on success or FALSE on failure. */ public function replace($key, $value, $group = 'default', $expiration = 0) { if (in_array($group, $this->nonPersistentGroups, true)) { if (!isset($this->nonPersistentStore[$group][$key])) { return false; } $this->nonPersistentStore[$group][$key] = $value; return true; } if (!static::getCache($group)->has($this->buildKey($group, $key))) { return false; } if ($expiration === 0) { static::getCache($group)->forever($this->buildKey($group, $key), $value); return true; } static::getCache($group)->put($this->buildKey($group, $key), $value, $expiration / 60); return true; } /** * Remove the item from the cache. * * @param string $key The key under which to store the value. * @param string $group The group value appended to the $key. * * @return bool Returns TRUE on success or FALSE on failure. */ public function delete($key, $group = 'default') { if (in_array($group, $this->nonPersistentGroups, true)) { if (isset($this->nonPersistentStore[$group][$key])) { unset($this->nonPersistentStore[$group][$key]); } return true; } return static::getCache($group)->forget($this->buildKey($group, $key)); } /** * Invalidate all items in the cache. * * @return bool Returns TRUE on success or FALSE on failure. */ public function flush() { $this->nonPersistentStore = []; if (Cache::getStore() instanceof TaggableStore) { Cache::tags(sprintf('%s:%s', static::PREFIX, config('cache.prefix')))->flush(); } else { // TODO: damn... cache driver doesn't support tags, should we loop over all cache elements and // delete anything that starts with our prefix? Cache::flush(); } return true; } /** * Retrieve object from cache. * * Gets an object from cache based on $key and $group. * * @param string $key The key under which to store the value. * @param string $group The group value appended to the $key. * @param bool $force Optional. Ignored * @param bool &$found Optional. Whether the key was found in the cache. Disambiguates a return of * false, a storable value. Passed by reference. Default null. * * @return bool|mixed Cached object value. */ public function get($key, $group = 'default', $force = false, &$found = null) { if (in_array($group, $this->nonPersistentGroups, true)) { if (!isset($this->nonPersistentStore[$group][$key])) { $found = false; return false; } $found = true; return $this->nonPersistentStore[$group][$key]; } $key = $this->buildKey($group, $key); if (static::getCache($group)->has($key)) { $this->cacheHits++; $found = true; } else { $this->cacheMisses++; $found = false; return false; } return maybe_unserialize(static::getCache($group)->get($key)); } /** * Increment a counter by the amount specified. * * @param string $key * @param int $offset * @param string $group * * @return int|bool False on failure, the item's new value on success. */ public function incr($key, $offset = 1, $group = 'default') { if (in_array($group, $this->nonPersistentGroups, true)) { if (!isset($this->nonPersistentStore[$group])) { $this->nonPersistentStore[$group] = []; } if (!isset($this->nonPersistentStore[$group][$key])) { $this->nonPersistentStore[$group][$key] = 0; } $this->nonPersistentStore[$group][$key]++; return true; } static::getCache($group)->increment($this->buildKey($group, $key), $offset); return static::getCache($group)->get($this->buildKey($group, $key)); } /** * Decrease a counter by the amount specified. * * @param string $key * @param int $offset * @param string $group * * @return int|bool False on failure, the item's new value on success. */ public function decr($key, $offset = 1, $group = 'default') { if (in_array($group, $this->nonPersistentGroups, true)) { if (!isset($this->nonPersistentStore[$group])) { $this->nonPersistentStore[$group] = []; } if (!isset($this->nonPersistentStore[$group][$key])) { $this->nonPersistentStore[$group][$key] = 0; } $this->nonPersistentStore[$group][$key]--; return true; } static::getCache($group)->decrement($this->buildKey($group, $key), $offset); return static::getCache($group)->get($this->buildKey($group, $key)); } /** * Sets a value in cache. * * The value is set whether or not this key already exists in our store. * * @param string $key The key under which to store the value. * @param mixed $value The value to store. * @param string $group The group value appended to the $key. * @param int $expiration The expiration time, defaults to 0. * * @return bool Returns TRUE on success or FALSE on failure. */ public function set($key, $value, $group = 'default', $expiration = 0) { if (in_array($group, $this->nonPersistentGroups, true)) { if (!isset($this->nonPersistentStore[$group])) { $this->nonPersistentStore[$group] = []; } $this->nonPersistentStore[$group][$key] = $value; return true; } if ($expiration === 0) { static::getCache($group)->forever($this->buildKey($group, $key), $value); return true; } static::getCache($group)->put($this->buildKey($group, $key), $value, $expiration / 60); return true; } /** * Sets the list of global cache groups. * * @param array $groups List of groups that are global. */ public function addGlobalGroups($groups) { $this->globalGroups = array_merge($this->globalGroups, (array) $groups); } /** * Sets the list of non-persistent cache groups. * * @param array $groups List of groups that should not be persisted. */ public function addNonPersistentGroups($groups) { $this->nonPersistentGroups = array_merge($this->nonPersistentGroups, (array) $groups); } /** * Get the cache driver we should be saving to and add a tag if the driver supports it. * * @param string $group * * @return Cache|\Illuminate\Cache\TaggedCache */ protected static function getCache($group = 'default') { return Cache::getStore() instanceof TaggableStore ? Cache::tags([ sprintf('%s:%s:%s', static::PREFIX . config('cache.prefix'), $group ?: 'default'), sprintf('%s:%s', static::PREFIX, config('cache.prefix')), ]) : cache(); } /** * Build a unique key for this cache object. * * @param string $group The group value appended to the $key. * @param string $key The key under which to store the value. * * @return string */ protected function buildKey($group, $key) { if (empty($group)) { $group = 'default'; } $prefix = (is_multisite() && !in_array($group, $this->globalGroups, true)) ? ($this->blogPrefix . ':') : ''; return sprintf('%s:%s%s:%s', static::PREFIX, $prefix, $group, $key); } }