mirror of
https://github.com/getgrav/grav.git
synced 2026-07-19 17:40:49 +02:00
Implement memory and chain cache
This commit is contained in:
177
system/src/Grav/Framework/Cache/Adapter/ChainCache.php
Normal file
177
system/src/Grav/Framework/Cache/Adapter/ChainCache.php
Normal file
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav\Framework\Cache
|
||||
*
|
||||
* @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Framework\Cache\Adapter;
|
||||
|
||||
use Grav\Framework\Cache\AbstractCache;
|
||||
use Grav\Framework\Cache\CacheInterface;
|
||||
|
||||
/**
|
||||
* Cache class for PSR-16 compatible "Simple Cache" implementation using chained cache adapters.
|
||||
*
|
||||
* @package Grav\Framework\Cache
|
||||
*/
|
||||
class ChainCache extends AbstractCache
|
||||
{
|
||||
/**
|
||||
* @var array|CacheInterface[]
|
||||
*/
|
||||
protected $caches;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $count;
|
||||
|
||||
/**
|
||||
* @var \stdClass
|
||||
*/
|
||||
protected $miss;
|
||||
|
||||
/**
|
||||
* Chain Cache constructor.
|
||||
* @param array $caches
|
||||
* @param null|int|\DateInterval $defaultLifetime
|
||||
*/
|
||||
public function __construct(array $caches, $defaultLifetime = null)
|
||||
{
|
||||
parent::__construct('', $defaultLifetime);
|
||||
|
||||
if (!$caches) {
|
||||
throw new \InvalidArgumentException('At least one cache must be specified');
|
||||
}
|
||||
|
||||
foreach ($caches as $cache) {
|
||||
if (!$cache instanceof CacheInterface) {
|
||||
throw new \InvalidArgumentException(sprintf("The class '%s' does not implement the '%s' interface", get_class($cache), CacheInterface::class));
|
||||
}
|
||||
}
|
||||
|
||||
$this->caches = array_values($caches);
|
||||
$this->count = count($caches);
|
||||
$this->miss = new \stdClass;
|
||||
}
|
||||
|
||||
public function doGet($key)
|
||||
{
|
||||
foreach ($this->caches as $i => $cache) {
|
||||
$value = $cache->doGet($key);
|
||||
if ($this->isHit($value)) {
|
||||
while (--$i >= 0) {
|
||||
// Update all the previous caches with missing value.
|
||||
$this->caches[$i]->doSet($key, $value, $this->getDefaultLifetime());
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->miss();
|
||||
}
|
||||
|
||||
public function doSet($key, $value, $ttl)
|
||||
{
|
||||
$success = true;
|
||||
$i = $this->count;
|
||||
|
||||
while ($i--) {
|
||||
$success = $this->caches[$i]->doSet($key, $value, $ttl) && $success;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
public function doDelete($key)
|
||||
{
|
||||
$success = true;
|
||||
$i = $this->count;
|
||||
|
||||
while ($i--) {
|
||||
$success = $this->caches[$i]->doDelete($key) && $success;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
public function doClear()
|
||||
{
|
||||
$success = true;
|
||||
$i = $this->count;
|
||||
|
||||
while ($i--) {
|
||||
$success = $this->caches[$i]->doClear() && $success;
|
||||
}
|
||||
return $success;
|
||||
}
|
||||
|
||||
|
||||
public function doGetMultiple($keys)
|
||||
{
|
||||
$found = [];
|
||||
$missing = [];
|
||||
foreach ($this->caches as $i => $cache) {
|
||||
$values = $cache->doGetMultiple($i ? $missing[$i - 1] : $keys);
|
||||
|
||||
foreach ($values as $key => $value) {
|
||||
if ($this->isHit($value)) {
|
||||
$found[$key] = $value;
|
||||
} else {
|
||||
$missing[$i][$key] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($missing[$i])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$values = [];
|
||||
// Update all the previous caches with missing values.
|
||||
foreach (array_reverse($missing) as $i => $keys) {
|
||||
$values += array_intersect($found, $keys);
|
||||
$this->caches[$i]->doSetMultiple($values, $this->getDefaultLifetime());
|
||||
}
|
||||
|
||||
return $found;
|
||||
}
|
||||
|
||||
public function doSetMultiple($values, $ttl)
|
||||
{
|
||||
$success = true;
|
||||
$i = $this->count;
|
||||
|
||||
while ($i--) {
|
||||
$success = $this->caches[$i]->doSetMultiple($values, $ttl) && $success;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
public function doDeleteMultiple($keys)
|
||||
{
|
||||
$success = true;
|
||||
$i = $this->count;
|
||||
|
||||
while ($i--) {
|
||||
$success = $this->caches[$i]->doDeleteMultiple($keys) && $success;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
public function doHas($key)
|
||||
{
|
||||
foreach ($this->caches as $cache) {
|
||||
if ($cache->doHas($key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -41,40 +41,40 @@ class DoctrineCache extends AbstractCache
|
||||
$this->driver = $doctrineCache;
|
||||
}
|
||||
|
||||
protected function doGet($key, $default)
|
||||
public function doGet($key)
|
||||
{
|
||||
$value = $this->driver->fetch($key);
|
||||
|
||||
// Doctrine cache does not differentiate between no result and cached 'false'. Make sure that we do.
|
||||
return $value !== false || $this->driver->contains($key) ? $value : $default;
|
||||
return $value !== false || $this->driver->contains($key) ? $value : $this->miss();
|
||||
}
|
||||
|
||||
protected function doSet($key, $value, $ttl)
|
||||
public function doSet($key, $value, $ttl)
|
||||
{
|
||||
return $this->driver->save($key, $value, (int) $ttl);
|
||||
}
|
||||
|
||||
protected function doDelete($key)
|
||||
public function doDelete($key)
|
||||
{
|
||||
return $this->driver->delete($key);
|
||||
}
|
||||
|
||||
protected function doClear()
|
||||
public function doClear()
|
||||
{
|
||||
return $this->driver->deleteAll();
|
||||
}
|
||||
|
||||
protected function doGetMultiple($keys, $default)
|
||||
public function doGetMultiple($keys)
|
||||
{
|
||||
return $this->driver->fetchMultiple($keys);
|
||||
}
|
||||
|
||||
protected function doSetMultiple($values, $ttl)
|
||||
public function doSetMultiple($values, $ttl)
|
||||
{
|
||||
return $this->driver->saveMultiple($values, (int) $ttl);
|
||||
}
|
||||
|
||||
protected function doDeleteMultiple($keys)
|
||||
public function doDeleteMultiple($keys)
|
||||
{
|
||||
// TODO: Remove when Doctrine Cache has been updated to support the feature.
|
||||
if (!method_exists($this->driver, 'deleteMultiple')) {
|
||||
@@ -89,7 +89,7 @@ class DoctrineCache extends AbstractCache
|
||||
return $this->driver->deleteMultiple($keys);
|
||||
}
|
||||
|
||||
protected function doHas($key)
|
||||
public function doHas($key)
|
||||
{
|
||||
return $this->driver->contains($key);
|
||||
}
|
||||
|
||||
@@ -12,12 +12,15 @@ use Grav\Framework\Cache\AbstractCache;
|
||||
|
||||
/**
|
||||
* Cache class for PSR-16 compatible "Simple Cache" implementation using in memory backend.
|
||||
* Memory backend does not use namespace or ttl as the cache is unique to each cache object and request.
|
||||
* Memory backend does not use namespace or default ttl as the cache is unique to each cache object and request.
|
||||
*
|
||||
* @package Grav\Framework\Cache
|
||||
*/
|
||||
class MemoryCache extends AbstractCache
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $cache = [];
|
||||
|
||||
/**
|
||||
@@ -28,34 +31,39 @@ class MemoryCache extends AbstractCache
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function doGet($key, $default)
|
||||
public function doGet($key)
|
||||
{
|
||||
return $this->doHas($key) ? $this->cache[$key] : $default;
|
||||
if (!array_key_exists($key, $this->cache)) {
|
||||
// Cache misses.
|
||||
$this->cache[$key] = $this->miss();
|
||||
}
|
||||
|
||||
return $this->cache[$key];
|
||||
}
|
||||
|
||||
protected function doSet($key, $value, $ttl)
|
||||
public function doSet($key, $value, $ttl)
|
||||
{
|
||||
$this->cache[$key] = $value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function doDelete($key)
|
||||
public function doDelete($key)
|
||||
{
|
||||
unset($this->cache[$key]);
|
||||
$this->cache[$key] = $this->miss();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function doClear()
|
||||
public function doClear()
|
||||
{
|
||||
$this->cache = [];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function doHas($key)
|
||||
public function doHas($key)
|
||||
{
|
||||
return array_key_exists($key, $this->cache);
|
||||
return array_key_exists($key, $this->cache) && $this->isHit($this->cache[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,4 +16,12 @@ use Psr\SimpleCache\CacheInterface as SimpleCacheInterface;
|
||||
*/
|
||||
interface CacheInterface extends SimpleCacheInterface
|
||||
{
|
||||
public function doGet($key);
|
||||
public function doSet($key, $value, $ttl);
|
||||
public function doDelete($key);
|
||||
public function doClear();
|
||||
public function doGetMultiple($keys);
|
||||
public function doSetMultiple($values, $ttl);
|
||||
public function doDeleteMultiple($keys);
|
||||
public function doHas($key);
|
||||
}
|
||||
|
||||
20
system/src/Grav/Framework/Cache/CacheMiss.php
Normal file
20
system/src/Grav/Framework/Cache/CacheMiss.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav\Framework\Cache
|
||||
*
|
||||
* @copyright Copyright (C) 2014 - 2017 RocketTheme, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Framework\Cache;
|
||||
|
||||
use Grav\Framework\Cache\CacheInterface;
|
||||
use Grav\Framework\Cache\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Represents Cache Miss
|
||||
* @package Grav\Framework\Cache
|
||||
*/
|
||||
class CacheMiss
|
||||
{
|
||||
}
|
||||
@@ -27,6 +27,11 @@ trait CacheTrait
|
||||
*/
|
||||
private $defaultLifetime = null;
|
||||
|
||||
/**
|
||||
* @var CacheMiss
|
||||
*/
|
||||
private $miss;
|
||||
|
||||
/**
|
||||
* Always call from constructor.
|
||||
*
|
||||
@@ -37,6 +42,7 @@ trait CacheTrait
|
||||
{
|
||||
$this->namespace = (string) $namespace;
|
||||
$this->defaultLifetime = $this->convertTtl($defaultLifetime, true);
|
||||
$this->miss = new CacheMiss();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,6 +61,23 @@ trait CacheTrait
|
||||
return $this->defaultLifetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
protected function isHit($value)
|
||||
{
|
||||
return !($value instanceof CacheMiss);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CacheMiss
|
||||
*/
|
||||
protected function miss()
|
||||
{
|
||||
return $this->miss;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
@@ -62,7 +85,9 @@ trait CacheTrait
|
||||
{
|
||||
$this->validateKey($key);
|
||||
|
||||
return $this->doGet($key, $default);
|
||||
$value = $this->doGet($key);
|
||||
|
||||
return $this->isHit($value) ? $value : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,7 +138,7 @@ trait CacheTrait
|
||||
|
||||
$this->validateKeys($keys);
|
||||
|
||||
$list = $this->doGetMultiple($keys, $default);
|
||||
$list = $this->doGetMultiple($keys);
|
||||
|
||||
if (count($list) !== count($keys)) {
|
||||
// Return all values, with default value if they do not exist.
|
||||
@@ -121,9 +146,7 @@ trait CacheTrait
|
||||
}
|
||||
|
||||
// Make sure that results are returned in the same order as the keys were given.
|
||||
ksort($list);
|
||||
|
||||
return $list;
|
||||
return array_replace(array_flip($keys), $list);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -181,6 +204,49 @@ trait CacheTrait
|
||||
return $this->doHas($key);
|
||||
}
|
||||
|
||||
abstract public function doGet($key);
|
||||
abstract public function doSet($key, $value, $ttl);
|
||||
abstract public function doDelete($key);
|
||||
abstract public function doClear();
|
||||
|
||||
public function doGetMultiple($keys)
|
||||
{
|
||||
$results = [];
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$value = $this->doGet($key);
|
||||
if ($this->isHit($value)) {
|
||||
$results[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function doSetMultiple($values, $ttl)
|
||||
{
|
||||
$success = true;
|
||||
|
||||
foreach ($values as $key => $value) {
|
||||
$success = $this->doSet($key, $value, $ttl) && $success;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
public function doDeleteMultiple($keys)
|
||||
{
|
||||
$success = true;
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$success = $this->doDelete($key) && $success;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
abstract public function doHas($key);
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*/
|
||||
@@ -228,46 +294,4 @@ trait CacheTrait
|
||||
|
||||
throw new InvalidArgumentException(sprintf('Expiration date must be an integer, a DateInterval or null, "%s" given', is_object($ttl) ? get_class($ttl) : gettype($ttl)));
|
||||
}
|
||||
|
||||
abstract protected function doGet($key, $default);
|
||||
abstract protected function doSet($key, $value, $ttl);
|
||||
abstract protected function doDelete($key);
|
||||
abstract protected function doClear();
|
||||
|
||||
protected function doGetMultiple($keys, $default)
|
||||
{
|
||||
$results = [];
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if ($this->doHas($key)) {
|
||||
$results[$key] = $this->doGet($key, $default);
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
protected function doSetMultiple($values, $ttl)
|
||||
{
|
||||
$success = true;
|
||||
|
||||
foreach ($values as $key => $value) {
|
||||
$success = $this->doSet($key, $value, $ttl) && $success;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
protected function doDeleteMultiple($keys)
|
||||
{
|
||||
$success = true;
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$success = $this->doDelete($key) && $success;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
abstract protected function doHas($key);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user