Replace Wordpress' caching with Laravel's cache driver
Diff
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(-)
@@ -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()
@@ -0,0 +1,374 @@
<?php
namespace Koselig\Proxy;
use Cache;
use Illuminate\Cache\TaggableStore;
/**
* Replace Wordpress' caching with Laravel's driver for consistency and, in many cases, speed.
*
* @author Jordan Doyle <jordan@doyle.wf>
*/
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);
}
}
@@ -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);
});
@@ -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
*