From 1c53483dbb18db6b3af033d099c92486ce54a15d Mon Sep 17 00:00:00 2001 From: Jordan Doyle Date: Sun, 4 Dec 2016 19:44:51 +0000 Subject: [PATCH] Replace Wordpress' caching with Laravel's cache driver --- src/Providers/WordpressServiceProvider.php | 11 +++-------- src/Proxy/WordpressCache.php | 374 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/Proxy/WordpressDatabase.php | 4 ++-- src/helpers.php | 2 +- 4 files changed, 380 insertions(+), 11 deletions(-) create mode 100644 src/Proxy/WordpressCache.php diff --git a/src/Providers/WordpressServiceProvider.php b/src/Providers/WordpressServiceProvider.php index e2edb00..b6ae7c2 100644 --- a/src/Providers/WordpressServiceProvider.php +++ b/src/Providers/WordpressServiceProvider.php @@ -24,13 +24,8 @@ class WordpressServiceProvider extends ServiceProvider public function register() { // get the path wordpress is installed in - define( - 'WP_PATH', - json_decode( - file_get_contents($this->app->basePath() . DIRECTORY_SEPARATOR . 'composer.json'), - true - )['extra']['wordpress-install-dir'] . '/' - ); + define('WP_PATH', json_decode(file_get_contents($this->app->basePath() . DIRECTORY_SEPARATOR . 'composer.json'), + true)['extra']['wordpress-install-dir'] . '/'); $this->setConfig(); @@ -42,7 +37,7 @@ class WordpressServiceProvider extends ServiceProvider * Bootstrap any application services. * * @SuppressWarnings(PHPMD.CamelCaseVariableName) - * + * @return void */ public function boot() diff --git a/src/Proxy/WordpressCache.php b/src/Proxy/WordpressCache.php new file mode 100644 index 0000000..9cdef7c --- /dev/null +++ b/src/Proxy/WordpressCache.php @@ -0,0 +1,374 @@ + + */ +class WordpressCache +{ + /** + * 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 = []; + + /** + * Prefix used for anything cached using the Wordpress API so it doesn't conflict + * with our normal caching. + */ + const PREFIX = 'wp'; + + /** + * 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)) { + 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)) { + 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)) { + 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 boolean $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)) { + 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)) { + 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)) { + 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)) { + 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 = 'default', $key) + { + if (empty($group)) { + $group = 'default'; + } + + $prefix = (is_multisite() && !in_array($group, $this->globalGroups)) ? ($this->blogPrefix . ':') : ''; + + return sprintf('%s:%s%s:%s', static::PREFIX, $prefix, $group, $key); + } +} diff --git a/src/Proxy/WordpressDatabase.php b/src/Proxy/WordpressDatabase.php index d1590f8..3b72958 100644 --- a/src/Proxy/WordpressDatabase.php +++ b/src/Proxy/WordpressDatabase.php @@ -236,10 +236,10 @@ class WordpressDatabase extends wpdb } else { if (!config('wordpress.caching')) { // remove cached query if caching has been disabled - Cache::forget('q_' . $query); + Cache::forget('q:' . $query); } - $this->result = Cache::remember('q_' . $query, config('wordpress.caching'), function () use ($query) { + $this->result = Cache::remember('q:' . $query, config('wordpress.caching'), function () use ($query) { return DB::select($query); }); diff --git a/src/helpers.php b/src/helpers.php index ab7c420..55f04fc 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -5,7 +5,7 @@ use Koselig\Models\Post; if (!function_exists('query')) { /** - * Get the main query or convert a query to a {@link \Koselig\Proxy\Query} proxy instance. + * Get the main query or convert a {@link WP_Query} to a {@link \Koselig\Proxy\Query} proxy instance. * * @param WP_Query|null $query * -- libgit2 1.7.2