3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
10 namespace Zend\Cache\Storage\Adapter
;
12 use Redis
as RedisResource
;
13 use RedisException
as RedisResourceException
;
15 use Zend\Cache\Storage\Adapter\AbstractAdapter
;
16 use Zend\Cache\Exception
;
17 use Zend\Cache\Storage\AvailableSpaceCapableInterface
;
18 use Zend\Cache\Storage\Capabilities
;
19 use Zend\Cache\Storage\FlushableInterface
;
20 use Zend\Cache\Storage\TotalSpaceCapableInterface
;
22 class Redis
extends AbstractAdapter
implements
24 TotalSpaceCapableInterface
28 * Has this instance be initialized
32 protected $initialized = false;
35 * The redis resource manager
37 * @var null|RedisResourceManager
39 protected $resourceManager;
42 * The redis resource id
46 protected $resourceId;
49 * The namespace prefix
53 protected $namespacePrefix = '';
56 * Create new Adapter for redis storage
58 * @param null|array|Traversable|RedisOptions $options
59 * @see \Zend\Cache\Storage\Adapter\Abstract
61 public function __construct($options = null)
63 if (!extension_loaded('redis')) {
64 throw new Exception\
ExtensionNotLoadedException("Redis extension is not loaded");
67 parent
::__construct($options);
69 // reset initialized flag on update option(s)
70 $initialized = & $this->initialized
;
71 $this->getEventManager()->attach('option', function ($event) use (& $initialized) {
79 * @return RedisResource
81 protected function getRedisResource()
83 if (!$this->initialized
) {
84 $options = $this->getOptions();
86 // get resource manager and resource id
87 $this->resourceManager
= $options->getResourceManager();
88 $this->resourceId
= $options->getResourceId();
90 // init namespace prefix
91 $namespace = $options->getNamespace();
92 if ($namespace !== '') {
93 $this->namespacePrefix
= $namespace . $options->getNamespaceSeparator();
95 $this->namespacePrefix
= '';
98 // update initialized flag
99 $this->initialized
= true;
102 return $this->resourceManager
->getResource($this->resourceId
);
110 * @param array|Traversable|RedisOptions $options
114 public function setOptions($options)
116 if (!$options instanceof RedisOptions
) {
117 $options = new RedisOptions($options);
119 return parent
::setOptions($options);
125 * @return RedisOptions
128 public function getOptions()
130 if (!$this->options
) {
131 $this->setOptions(new RedisOptions());
133 return $this->options
;
137 * Internal method to get an item.
139 * @param string &$normalizedKey Key where to store data
140 * @param bool &$success If the operation was successfull
141 * @param mixed &$casToken Token
142 * @return mixed Data on success, false on key not found
143 * @throws Exception\RuntimeException
145 protected function internalGetItem(& $normalizedKey, & $success = null, & $casToken = null)
147 $redis = $this->getRedisResource();
149 $value = $redis->get($this->namespacePrefix
. $normalizedKey);
150 } catch (RedisResourceException
$e) {
151 throw new Exception\
RuntimeException($redis->getLastError(), $e->getCode(), $e);
154 if ($value === false) {
165 * Internal method to get multiple items.
167 * @param array &$normalizedKeys Array of keys to be obtained
169 * @return array Associative array of keys and values
170 * @throws Exception\RuntimeException
172 protected function internalGetItems(array & $normalizedKeys)
174 $redis = $this->getRedisResource();
176 $namespacedKeys = array();
177 foreach ($normalizedKeys as & $normalizedKey) {
178 $namespacedKeys[] = $this->namespacePrefix
. $normalizedKey;
182 $results = $redis->mGet($namespacedKeys);
183 } catch (RedisResourceException
$e) {
184 throw new Exception\
RuntimeException($redis->getLastError(), $e->getCode(), $e);
186 //combine the key => value pairs and remove all missing values
188 array_combine($normalizedKeys, $results),
190 return $value !== false;
196 * Internal method to test if an item exists.
198 * @param string &$normalizedKey Normalized key which will be checked
201 * @throws Exception\RuntimeException
203 protected function internalHasItem(& $normalizedKey)
205 $redis = $this->getRedisResource();
207 return $redis->exists($this->namespacePrefix
. $normalizedKey);
208 } catch (RedisResourceException
$e) {
209 throw new Exception\
RuntimeException($redis->getLastError(), $e->getCode(), $e);
214 * Internal method to store an item.
216 * @param string &$normalizedKey Key in Redis under which value will be saved
217 * @param mixed &$value Value to store under cache key
220 * @throws Exception\RuntimeException
222 protected function internalSetItem(& $normalizedKey, & $value)
224 $redis = $this->getRedisResource();
225 $ttl = $this->getOptions()->getTtl();
229 if ($this->resourceManager
->getMajorVersion($this->resourceId
) < 2) {
230 throw new Exception\
UnsupportedMethodCallException("To use ttl you need version >= 2.0.0");
232 $success = $redis->setex($this->namespacePrefix
. $normalizedKey, $ttl, $value);
234 $success = $redis->set($this->namespacePrefix
. $normalizedKey, $value);
236 } catch (RedisResourceException
$e) {
237 throw new Exception\
RuntimeException($redis->getLastError(), $e->getCode(), $e);
244 * Internal method to store multiple items.
246 * @param array &$normalizedKeyValuePairs An array of normalized key/value pairs
248 * @return array Array of not stored keys
249 * @throws Exception\RuntimeException
251 protected function internalSetItems(array & $normalizedKeyValuePairs)
253 $redis = $this->getRedisResource();
254 $ttl = $this->getOptions()->getTtl();
256 $namespacedKeyValuePairs = array();
257 foreach ($normalizedKeyValuePairs as $normalizedKey => & $value) {
258 $namespacedKeyValuePairs[$this->namespacePrefix
. $normalizedKey] = & $value;
262 //check if ttl is supported
263 if ($this->resourceManager
->getMajorVersion($this->resourceId
) < 2) {
264 throw new Exception\
UnsupportedMethodCallException("To use ttl you need version >= 2.0.0");
266 //mSet does not allow ttl, so use transaction
267 $transaction = $redis->multi();
268 foreach ($namespacedKeyValuePairs as $key => $value) {
269 $transaction->setex($key, $ttl, $value);
271 $success = $transaction->exec();
273 $success = $redis->mSet($namespacedKeyValuePairs);
276 } catch (RedisResourceException
$e) {
277 throw new Exception\
RuntimeException($redis->getLastError(), $e->getCode(), $e);
280 throw new Exception\
RuntimeException($redis->getLastError());
289 * @param string $normalizedKey
290 * @param mixed $value
292 * @throws Exception\RuntimeException
294 protected function internalAddItem(& $normalizedKey, & $value)
296 $redis = $this->getRedisResource();
298 return $redis->setnx($this->namespacePrefix
. $normalizedKey, $value);
299 } catch (RedisResourceException
$e) {
300 throw new Exception\
RuntimeException($redis->getLastError(), $e->getCode(), $e);
305 * Internal method to remove an item.
307 * @param string &$normalizedKey Key which will be removed
310 * @throws Exception\RuntimeException
312 protected function internalRemoveItem(& $normalizedKey)
314 $redis = $this->getRedisResource();
316 return (bool) $redis->delete($this->namespacePrefix
. $normalizedKey);
317 } catch (RedisResourceException
$e) {
318 throw new Exception\
RuntimeException($redis->getLastError(), $e->getCode(), $e);
323 * Internal method to increment an item.
325 * @param string $normalizedKey
327 * @return int|bool The new value on success, false on failure
328 * @throws Exception\RuntimeException
330 protected function internalIncrementItem(& $normalizedKey, & $value)
332 $redis = $this->getRedisResource();
334 return $redis->incrBy($this->namespacePrefix
. $normalizedKey, $value);
335 } catch (RedisResourceException
$e) {
336 throw new Exception\
RuntimeException($redis->getLastError(), $e->getCode(), $e);
341 * Internal method to decrement an item.
343 * @param string $normalizedKey
345 * @return int|bool The new value on success, false on failure
346 * @throws Exception\RuntimeException
348 protected function internalDecrementItem(& $normalizedKey, & $value)
350 $redis = $this->getRedisResource();
352 return $redis->decrBy($this->namespacePrefix
. $normalizedKey, $value);
353 } catch (RedisResourceException
$e) {
354 throw new Exception\
RuntimeException($redis->getLastError(), $e->getCode(), $e);
359 * Flush currently set DB
362 * @throws Exception\RuntimeException
364 public function flush()
366 $redis = $this->getRedisResource();
368 return $redis->flushDB();
369 } catch (RedisResourceException
$e) {
370 throw new Exception\
RuntimeException($redis->getLastError(), $e->getCode(), $e);
374 /* TotalSpaceCapableInterface */
377 * Get total space in bytes
381 public function getTotalSpace()
383 $redis = $this->getRedisResource();
385 $info = $redis->info();
386 } catch (RedisResourceException
$e) {
387 throw new Exception\
RuntimeException($redis->getLastError(), $e->getCode(), $e);
390 return $info['used_memory'];
397 * Internal method to get capabilities of this adapter
399 * @return Capabilities
401 protected function internalGetCapabilities()
403 if ($this->capabilities
=== null) {
404 $this->capabilityMarker
= new stdClass();
405 $minTtl = $this->resourceManager
->getMajorVersion($this->resourceId
) < 2 ?
0 : 1;
406 //without serialization redis supports only strings for simple
408 $this->capabilities
= new Capabilities(
410 $this->capabilityMarker
,
412 'supportedDatatypes' => array(
414 'boolean' => 'string',
415 'integer' => 'string',
416 'double' => 'string',
422 'supportedMetadata' => array(),
427 'useRequestTime' => false,
428 'expiredRead' => false,
429 'maxKeyLength' => 255,
430 'namespaceIsPrefix' => true,
435 return $this->capabilities
;