* @copyright 2016 - 2019 Timothy J. Warren * @license http://www.opensource.org/licenses/mit-license.html MIT License * @version 3.0.0 * @link https://git.timshomepage.net/timw4mail/banker */ namespace Aviat\Banker; use Aviat\Banker\Driver\DriverInterface; use Aviat\Banker\Exception\InvalidArgumentException; use Aviat\Banker\{Item, ItemCollection}; use Psr\Cache\{CacheItemInterface, CacheItemPoolInterface}; use Psr\Log\{LoggerAwareInterface, LoggerInterface}; /** * The main cache manager */ final class Pool implements CacheItemPoolInterface, LoggerAwareInterface { use LoggerTrait; /** * Driver class for handling the chosen caching backend * * @var DriverInterface */ protected $driver; /** * Cache Items to be saved * * @var array */ protected $deferred = []; /** * Set up the cache backend * * @param array $config * @param LoggerInterface $logger */ public function __construct(array $config, ?LoggerInterface $logger = NULL) { $this->driver = $this->loadDriver($config); if ($logger !== NULL) { $this->setLogger($logger); } } /** * Returns a Cache Item representing the specified key. * * This method must always return a CacheItemInterface object, even in case of * a cache miss. It MUST NOT return null. * * @param string $key * The key for which to return the corresponding Cache Item. * * @throws InvalidArgumentException * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException * MUST be thrown. * * @return CacheItemInterface * The corresponding Cache Item. */ public function getItem($key): CacheItemInterface { if ( ! \is_string($key)) { throw new InvalidArgumentException(); } // If a deferred item exists, return that if (array_key_exists($key, $this->deferred)) { return $this->deferred[$key]; } return new Item($this->driver, $key); } /** * Returns a traversable set of cache items. * * @param string[] $keys * An indexed array of keys of items to retrieve. * * @throws InvalidArgumentException * If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException * MUST be thrown. * * @return array|\Traversable * A traversable collection of Cache Items keyed by the cache keys of * each item. A Cache item will be returned for each key, even if that * key is not found. However, if no keys are specified then an empty * traversable MUST be returned instead. */ public function getItems(array $keys = []) { if (empty($keys)) { return new ItemCollection([]); } // Get the set of items selected $items = []; foreach($keys as $key) { if ( ! \is_string($key)) { throw new InvalidArgumentException(); } $items[$key] = array_key_exists($key, $this->deferred) ? $this->deferred[$key] : new Item($this->driver, $key); } return new ItemCollection($items); } /** * Confirms if the cache contains specified cache item. * * Note: This method MAY avoid retrieving the cached value for performance reasons. * This could result in a race condition with CacheItemInterface::get(). To avoid * such situation use CacheItemInterface::isHit() instead. * * @param string $key * The key for which to check existence. * * @throws InvalidArgumentException * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException * MUST be thrown. * * @return bool * True if item exists in the cache, false otherwise. */ public function hasItem($key): bool { if ( ! \is_string($key)) { throw new InvalidArgumentException(); } // See if there are any deferred items if (array_key_exists($key, $this->deferred)) { return TRUE; } return $this->driver->exists($key); } /** * Deletes all items in the pool. * * @return bool * True if the pool was successfully cleared. False if there was an error. */ public function clear(): bool { return $this->driver->flush(); } /** * Removes the item from the pool. * * @param string $key * The key to delete. * * @throws InvalidArgumentException * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException * MUST be thrown. * * @return bool * True if the item was successfully removed. False if there was an error. */ public function deleteItem($key): bool { if ( ! \is_string($key)) { throw new InvalidArgumentException(); } if ( ! $this->hasItem($key)) { return FALSE; } return $this->driver->delete($key); } /** * Removes multiple items from the pool. * * @param string[] $keys * An array of keys that should be removed from the pool. * @throws InvalidArgumentException * If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException * MUST be thrown. * * @return bool * True if the items were successfully removed. False if there was an error. */ public function deleteItems(array $keys): bool { foreach ($keys as $key) { if ( ! \is_string($key)) { throw new InvalidArgumentException(); } } return $this->driver->deleteMultiple($keys); } /** * Persists a cache item immediately. * * @param CacheItemInterface $item * The cache item to save. * * @return bool * True if the item was successfully persisted. False if there was an error. */ public function save(CacheItemInterface $item): bool { return $item->save(); } /** * Sets a cache item to be persisted later. * * @param CacheItemInterface $item * The cache item to save. * * @return bool * False if the item could not be queued or if a commit was attempted and failed. True otherwise. */ public function saveDeferred(CacheItemInterface $item): bool { $key = $item->getKey(); $this->deferred[$key] = $item; return TRUE; } /** * Persists any deferred cache items. * * @return bool * True if all not-yet-saved items were successfully saved or there were none. False otherwise. */ public function commit(): bool { if (empty($this->deferred)) { return TRUE; } $result = TRUE; foreach($this->deferred as $item) { $result = $result && $this->save($item); } if ($result === TRUE) { $this->deferred = []; } return $result; } /** * Instantiate the appropriate cache backend based on the config * * @param array $driverConfig * @return DriverInterface */ protected function loadDriver(array $driverConfig = []): DriverInterface { $driver = ucfirst(strtolower($driverConfig['driver'] ?? 'null')); $class = __NAMESPACE__ . "\\Driver\\${driver}Driver"; $driverConfig['connection'] = $driverConfig['connection'] ?? []; $driverConfig['options'] = $driverConfig['options'] ?? []; return new $class($driverConfig['connection'], $driverConfig['options']); } }