123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- <?php
- /**
- * Cache Class for Redis
- *
- * Taken from Yoshiharu Shibata <shibata@zoga.me>
- * and Chris Go <chris@velocimedia.com>
- *
- * - requires PhpRedis
- *
- * @package KO7\Cache
- * @category Base
- *
- * @copyright (c) Koseven Team
- * @license https://koseven.dev/LICENSE
- */
- class KO7_Cache_Redis extends Cache implements Cache_Tagging
- {
- /**
- * Redis instance
- * @var Redis
- */
- protected $_redis;
- /**
- * Prefix for tag
- * @var string
- */
- protected $_tag_prefix = '_tag';
- /**
- * Ensures singleton pattern is observed, loads the default expiry
- *
- * @param array $config Configuration
- * @throws Cache_Exception Redis ext. not loaded or server not configured
- */
- public function __construct(array $config)
- {
- if ( ! extension_loaded('redis'))
- {
- // @codeCoverageIgnoreStart
- throw new Cache_Exception(__METHOD__.' Redis PHP extension not loaded!');
- // @codeCoverageIgnoreEnd
- }
- parent::__construct($config);
- // Get Configured Servers
- $servers = Arr::get($this->_config, 'servers', NULL);
- if (empty($servers))
- {
- throw new Cache_Exception('No Redis servers defined in configuration');
- }
- $this->_redis = new Redis();
- // Global cache prefix so the keys in redis is organized
- $cache_prefix = Arr::get($this->_config, 'cache_prefix', NULL);
- $this->_tag_prefix = Arr::get($this->_config, 'tag_prefix', $this->_tag_prefix). ':';
- foreach($servers as $server)
- {
- // Connection method
- $method = Arr::get($server, 'persistent', FALSE) ? 'pconnect': 'connect';
- $this->_redis->{$method}($server['host'], $server['port'], 1);
- // See if there is a password
- $password = Arr::get($server, 'password', NULL);
- if ( ! empty($password))
- {
- $this->_redis->auth($password);
- }
- // Prefix a name space
- $prefix = Arr::get($server, 'prefix', NULL);
- if ( ! empty($prefix))
- {
- if ( ! empty($cache_prefix))
- {
- $prefix .= ':'.$cache_prefix;
- }
- $prefix .= ':';
- $this->_redis->setOption(Redis::OPT_PREFIX, $prefix);
- }
- }
- // serialize stuff
- // if use Redis::SERIALIZER_IGBINARY, "run configure with --enable-redis-igbinary"
- $this->_redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
- }
- /**
- * Get value from cache
- *
- * @param array|string $id
- * @param mixed $default
- * @return mixed
- */
- public function get($id, $default = NULL)
- {
- $value = NULL;
- if (is_array($id))
- {
- // sanitize keys
- $ids = array_map([$this, '_sanitize_id'], $id);
- // return key/value
- $value = array_combine($id, $this->_redis->mget($ids));
- }
- else
- {
- // sanitize keys
- $id = $this->_sanitize_id($id);
- $value = $this->_redis->get($id);
- }
- return empty($value) ? $default : $value;
- }
- /**
- * Set value
- * - supports multi set but assumes count of ids == count of data
- *
- * @param array|string $id Cache Key or assoc
- * @param mixed $data Cache Value
- * @param int $lifetime Cache lifetime
- * @return bool|null Set|NotSet
- */
- public function set($id, $data, $lifetime = 3600)
- {
- if (is_array($id))
- {
- // sanitize keys
- $ids = array_map([$this, '_sanitize_id'], $id);
- // use mset to put it all in redis
- $set = $this->_redis->mset(array_combine($ids, array_values($data)));
- $this->_set_ttl($ids, $lifetime); // give it an array of keys and one lifetime
- }
- else
- {
- $id = $this->_sanitize_id($id);
- $set = $this->_redis->mset([$id => $data]);
- $this->_set_ttl($id, $lifetime);
- }
- return $set;
- }
- /**
- * Delete Value
- *
- * @param string $id Cached Key
- * @return bool Number of Keys deleted
- */
- public function delete($id) : bool
- {
- $id = $this->_sanitize_id($id);
- return $this->_redis->del($id) >= 1;
- }
- /**
- * Delete all values
- *
- * @return bool Always True
- */
- public function delete_all() : bool
- {
- return $this->_redis->flushDB();
- }
- /**
- * Set the lifetime
- *
- * @param mixed $keys Cache Key or Array
- * @param int $lifetime Lifetime in seconds
- */
- protected function _set_ttl($keys, $lifetime = null)
- {
- // If lifetime is null
- if ($lifetime === NULL AND $lifetime !== 0)
- {
- $lifetime = Arr::get($this->_config, 'default_expire', 3600);
- }
- if ($lifetime > 0) {
- if (is_array($keys))
- {
- foreach ($keys as $key)
- {
- $this->_redis->expire($key, $lifetime);
- }
- }
- else
- {
- $this->_redis->expire($keys, $lifetime);
- }
- }
- }
- // ==================== TAGS ====================
- /**
- * Set a value based on an id with tags
- *
- * @param string $id Cache Key
- * @param mixed $data Cache Data
- * @param integer $lifetime Lifetime [Optional]
- * @param array $tags Tags [Optional]
- * @return bool|null Set|NotSet
- */
- public function set_with_tags($id, $data, $lifetime = NULL, array $tags = NULL)
- {
- $id = $this->_sanitize_id($id);
- $result = $this->set($id, $data, $lifetime);
- if ($result AND $tags)
- {
- foreach ($tags as $tag)
- {
- $this->_redis->lPush($this->_tag_prefix.$tag, $id);
- }
- }
- return $result;
- }
- /**
- * Delete cache entries based on a tag
- *
- * @param string $tag Tag
- * @return bool Deleted?
- */
- public function delete_tag($tag) : bool
- {
- if ($this->_redis->exists($this->_tag_prefix.$tag)) {
- $keys = $this->_redis->lRange($this->_tag_prefix.$tag, 0, -1);
- if (!empty($keys) AND count($keys))
- {
- foreach ($keys as $key)
- {
- $this->delete($key);
- }
- }
- // Then delete the tag itself
- $this->_redis->del($this->_tag_prefix.$tag);
- return TRUE;
- }
- return FALSE;
- }
- /**
- * Find cache entries based on a tag
- *
- * @param string $tag Tag
- * @return null
- */
- public function find($tag)
- {
- if ($this->_redis->exists($this->_tag_prefix.$tag))
- {
- $keys = $this->_redis->lRange($this->_tag_prefix.$tag, 0, -1);
- if (!empty($keys) AND count($keys))
- {
- $rows = [];
- foreach ($keys as $key)
- {
- $rows[$key] = $this->get($key);
- }
- return $rows;
- }
- }
- return NULL;
- }
- }
|