[Back] <?php
namespace Illuminate\Cache;
use Illuminate\Cache\Events\CacheFailedOver;
use Illuminate\Contracts\Cache\LockProvider;
use Illuminate\Contracts\Events\Dispatcher;
use RuntimeException;
use Throwable;
class FailoverStore extends TaggableStore implements LockProvider
{
/**
* The caches which failed on the last action.
*
* @var list<string>
*/
protected array $failingCaches = [];
/**
* Create a new failover store.
*
* @param array<int, string> $stores
*/
public function __construct(
protected CacheManager $cache,
protected Dispatcher $events,
protected array $stores
) {
}
/**
* Retrieve an item from the cache by key.
*
* @param string $key
* @return mixed
*/
public function get($key)
{
return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
}
/**
* Retrieve multiple items from the cache by key.
*
* Items not found in the cache will have a null value.
*
* @return array
*/
public function many(array $keys)
{
return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
}
/**
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param int $seconds
* @return bool
*/
public function put($key, $value, $seconds)
{
return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
}
/**
* Store multiple items in the cache for a given number of seconds.
*
* @param int $seconds
* @return bool
*/
public function putMany(array $values, $seconds)
{
return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
}
/**
* Store an item in the cache if the key doesn't exist.
*
* @param string $key
* @param mixed $value
* @param int $seconds
* @return bool
*/
public function add($key, $value, $seconds)
{
return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
}
/**
* Increment the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @return int|false
*/
public function increment($key, $value = 1)
{
return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
}
/**
* Decrement the value of an item in the cache.
*
* @param string $key
* @param mixed $value
* @return int|false
*/
public function decrement($key, $value = 1)
{
return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
}
/**
* Store an item in the cache indefinitely.
*
* @param string $key
* @param mixed $value
* @return bool
*/
public function forever($key, $value)
{
return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
}
/**
* Get a lock instance.
*
* @param string $name
* @param int $seconds
* @param string|null $owner
* @return \Illuminate\Contracts\Cache\Lock
*/
public function lock($name, $seconds = 0, $owner = null)
{
return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
}
/**
* Restore a lock instance using the owner identifier.
*
* @param string $name
* @param string $owner
* @return \Illuminate\Contracts\Cache\Lock
*/
public function restoreLock($name, $owner)
{
return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
}
/**
* Remove an item from the cache.
*
* @param string $key
* @return bool
*/
public function forget($key)
{
return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
}
/**
* Remove all items from the cache.
*
* @return bool
*/
public function flush()
{
return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
}
/**
* Remove all expired tag set entries.
*
* @return void
*/
public function flushStaleTags()
{
foreach ($this->stores as $store) {
if ($this->store($store)->getStore() instanceof RedisStore) {
$this->store($store)->flushStaleTags();
break;
}
}
}
/**
* Get the cache key prefix.
*
* @return string
*/
public function getPrefix()
{
return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
}
/**
* Attempt the given method on all stores.
*
* @return mixed
*
* @throws \Throwable
*/
protected function attemptOnAllStores(string $method, array $arguments)
{
[$lastException, $failedCaches] = [null, []];
try {
foreach ($this->stores as $store) {
try {
return $this->store($store)->{$method}(...$arguments);
} catch (Throwable $e) {
$lastException = $e;
$failedCaches[] = $store;
if (! in_array($store, $this->failingCaches)) {
$this->events->dispatch(new CacheFailedOver($store, $e));
}
}
}
} finally {
$this->failingCaches = $failedCaches;
}
throw $lastException ?? new RuntimeException('All failover cache stores failed.');
}
/**
* Get the cache store for the given store name.
*
* @return \Illuminate\Contracts\Cache\Repository
*/
protected function store(string $store)
{
return $this->cache->store($store);
}
}