torrentpier-lts/library/Zend/Cache/Storage/Adapter/Redis.php

462 lines
14 KiB
PHP
Raw Permalink Normal View History

<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
2023-04-01 09:03:34 +03:00
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use Redis as RedisResource;
use RedisException as RedisResourceException;
use stdClass;
use Traversable;
2023-04-01 09:03:34 +03:00
use Zend\Cache\Storage\ClearByPrefixInterface;
use Zend\Cache\Exception;
use Zend\Cache\Storage\Capabilities;
use Zend\Cache\Storage\FlushableInterface;
use Zend\Cache\Storage\TotalSpaceCapableInterface;
class Redis extends AbstractAdapter implements
2023-04-01 09:03:34 +03:00
ClearByPrefixInterface,
FlushableInterface,
TotalSpaceCapableInterface
{
/**
* Has this instance be initialized
*
* @var bool
*/
protected $initialized = false;
/**
* The redis resource manager
*
* @var null|RedisResourceManager
*/
protected $resourceManager;
/**
* The redis resource id
*
* @var null|string
*/
protected $resourceId;
/**
* The namespace prefix
*
* @var string
*/
protected $namespacePrefix = '';
/**
* Create new Adapter for redis storage
*
* @param null|array|Traversable|RedisOptions $options
* @see \Zend\Cache\Storage\Adapter\Abstract
*/
public function __construct($options = null)
{
if (!extension_loaded('redis')) {
throw new Exception\ExtensionNotLoadedException("Redis extension is not loaded");
}
parent::__construct($options);
// reset initialized flag on update option(s)
$initialized = & $this->initialized;
2023-04-01 09:03:34 +03:00
$this->getEventManager()->attach('option', function () use (& $initialized) {
$initialized = false;
});
}
/**
* Get Redis resource
*
* @return RedisResource
*/
protected function getRedisResource()
{
if (!$this->initialized) {
$options = $this->getOptions();
// get resource manager and resource id
$this->resourceManager = $options->getResourceManager();
$this->resourceId = $options->getResourceId();
// init namespace prefix
$namespace = $options->getNamespace();
if ($namespace !== '') {
$this->namespacePrefix = $namespace . $options->getNamespaceSeparator();
} else {
$this->namespacePrefix = '';
}
// update initialized flag
$this->initialized = true;
}
return $this->resourceManager->getResource($this->resourceId);
}
/* options */
/**
* Set options.
*
* @param array|Traversable|RedisOptions $options
* @return Redis
* @see getOptions()
*/
public function setOptions($options)
{
if (!$options instanceof RedisOptions) {
$options = new RedisOptions($options);
}
return parent::setOptions($options);
}
/**
* Get options.
*
* @return RedisOptions
* @see setOptions()
*/
public function getOptions()
{
if (!$this->options) {
$this->setOptions(new RedisOptions());
}
return $this->options;
}
/**
* Internal method to get an item.
*
* @param string &$normalizedKey Key where to store data
* @param bool &$success If the operation was successfull
* @param mixed &$casToken Token
* @return mixed Data on success, false on key not found
* @throws Exception\RuntimeException
*/
protected function internalGetItem(& $normalizedKey, & $success = null, & $casToken = null)
{
$redis = $this->getRedisResource();
try {
$value = $redis->get($this->namespacePrefix . $normalizedKey);
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
if ($value === false) {
$success = false;
2023-04-01 09:03:34 +03:00
return;
}
$success = true;
$casToken = $value;
return $value;
}
/**
* Internal method to get multiple items.
*
* @param array &$normalizedKeys Array of keys to be obtained
*
* @return array Associative array of keys and values
* @throws Exception\RuntimeException
*/
protected function internalGetItems(array & $normalizedKeys)
{
$redis = $this->getRedisResource();
$namespacedKeys = array();
foreach ($normalizedKeys as $normalizedKey) {
$namespacedKeys[] = $this->namespacePrefix . $normalizedKey;
}
try {
$results = $redis->mGet($namespacedKeys);
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
//combine the key => value pairs and remove all missing values
return array_filter(
array_combine($normalizedKeys, $results),
function ($value) {
return $value !== false;
}
);
}
/**
* Internal method to test if an item exists.
*
* @param string &$normalizedKey Normalized key which will be checked
*
* @return bool
* @throws Exception\RuntimeException
*/
protected function internalHasItem(& $normalizedKey)
{
$redis = $this->getRedisResource();
try {
return $redis->exists($this->namespacePrefix . $normalizedKey);
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
}
/**
* Internal method to store an item.
*
* @param string &$normalizedKey Key in Redis under which value will be saved
* @param mixed &$value Value to store under cache key
*
* @return bool
* @throws Exception\RuntimeException
*/
protected function internalSetItem(& $normalizedKey, & $value)
{
$redis = $this->getRedisResource();
$ttl = $this->getOptions()->getTtl();
try {
if ($ttl) {
if ($this->resourceManager->getMajorVersion($this->resourceId) < 2) {
throw new Exception\UnsupportedMethodCallException("To use ttl you need version >= 2.0.0");
}
$success = $redis->setex($this->namespacePrefix . $normalizedKey, $ttl, $value);
} else {
$success = $redis->set($this->namespacePrefix . $normalizedKey, $value);
}
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
return $success;
}
/**
* Internal method to store multiple items.
*
* @param array &$normalizedKeyValuePairs An array of normalized key/value pairs
*
* @return array Array of not stored keys
* @throws Exception\RuntimeException
*/
protected function internalSetItems(array & $normalizedKeyValuePairs)
{
$redis = $this->getRedisResource();
$ttl = $this->getOptions()->getTtl();
$namespacedKeyValuePairs = array();
foreach ($normalizedKeyValuePairs as $normalizedKey => $value) {
$namespacedKeyValuePairs[$this->namespacePrefix . $normalizedKey] = $value;
}
try {
if ($ttl > 0) {
//check if ttl is supported
if ($this->resourceManager->getMajorVersion($this->resourceId) < 2) {
throw new Exception\UnsupportedMethodCallException("To use ttl you need version >= 2.0.0");
}
//mSet does not allow ttl, so use transaction
$transaction = $redis->multi();
foreach ($namespacedKeyValuePairs as $key => $value) {
$transaction->setex($key, $ttl, $value);
}
$success = $transaction->exec();
} else {
$success = $redis->mSet($namespacedKeyValuePairs);
}
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
if (!$success) {
throw new Exception\RuntimeException($redis->getLastError());
}
return array();
}
/**
* Add an item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\RuntimeException
*/
protected function internalAddItem(& $normalizedKey, & $value)
{
$redis = $this->getRedisResource();
try {
return $redis->setnx($this->namespacePrefix . $normalizedKey, $value);
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
}
/**
* Internal method to remove an item.
*
* @param string &$normalizedKey Key which will be removed
*
* @return bool
* @throws Exception\RuntimeException
*/
protected function internalRemoveItem(& $normalizedKey)
{
$redis = $this->getRedisResource();
try {
return (bool) $redis->delete($this->namespacePrefix . $normalizedKey);
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
}
/**
* Internal method to increment an item.
*
* @param string $normalizedKey
* @param int $value
* @return int|bool The new value on success, false on failure
* @throws Exception\RuntimeException
*/
protected function internalIncrementItem(& $normalizedKey, & $value)
{
$redis = $this->getRedisResource();
try {
return $redis->incrBy($this->namespacePrefix . $normalizedKey, $value);
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
}
/**
* Internal method to decrement an item.
*
* @param string $normalizedKey
* @param int $value
* @return int|bool The new value on success, false on failure
* @throws Exception\RuntimeException
*/
protected function internalDecrementItem(& $normalizedKey, & $value)
{
$redis = $this->getRedisResource();
try {
return $redis->decrBy($this->namespacePrefix . $normalizedKey, $value);
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
}
/**
* Flush currently set DB
*
* @return bool
* @throws Exception\RuntimeException
*/
public function flush()
{
$redis = $this->getRedisResource();
try {
return $redis->flushDB();
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
}
2023-04-01 09:03:34 +03:00
/* ClearByPrefixInterface */
/**
* Remove items matching given prefix
*
* @param string $prefix
* @return bool
*/
public function clearByPrefix($prefix)
{
$redis = $this->getRedisResource();
$prefix = (string) $prefix;
if ($prefix === '') {
throw new Exception\InvalidArgumentException('No prefix given');
}
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator() . $prefix;
$redis->delete($redis->keys($prefix.'*'));
return true;
}
/* TotalSpaceCapableInterface */
/**
* Get total space in bytes
*
* @return int|float
*/
public function getTotalSpace()
{
$redis = $this->getRedisResource();
try {
$info = $redis->info();
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
return $info['used_memory'];
}
/* status */
/**
* Internal method to get capabilities of this adapter
*
* @return Capabilities
*/
protected function internalGetCapabilities()
{
if ($this->capabilities === null) {
$this->capabilityMarker = new stdClass();
$minTtl = $this->resourceManager->getMajorVersion($this->resourceId) < 2 ? 0 : 1;
//without serialization redis supports only strings for simple
//get/set methods
$this->capabilities = new Capabilities(
$this,
$this->capabilityMarker,
array(
'supportedDatatypes' => array(
'NULL' => 'string',
'boolean' => 'string',
'integer' => 'string',
'double' => 'string',
'string' => true,
'array' => false,
'object' => false,
'resource' => false,
),
'supportedMetadata' => array(),
'minTtl' => $minTtl,
'maxTtl' => 0,
'staticTtl' => true,
'ttlPrecision' => 1,
'useRequestTime' => false,
'expiredRead' => false,
'maxKeyLength' => 255,
'namespaceIsPrefix' => true,
)
);
}
return $this->capabilities;
}
}