mirror of
https://github.com/torrentpier/torrentpier-lts.git
synced 2025-03-01 15:21:02 +03:00
434 lines
13 KiB
PHP
434 lines
13 KiB
PHP
<?php
|
|
/**
|
|
* Zend Framework (http://framework.zend.com/)
|
|
*
|
|
* @link http://github.com/zendframework/zf2 for the canonical source repository
|
|
* @copyright Copyright (c) 2005-2014 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;
|
|
use Zend\Cache\Exception;
|
|
use Zend\Cache\Storage\Capabilities;
|
|
use Zend\Cache\Storage\FlushableInterface;
|
|
use Zend\Cache\Storage\TotalSpaceCapableInterface;
|
|
|
|
class Redis extends AbstractAdapter implements
|
|
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;
|
|
$this->getEventManager()->attach('option', function ($event) 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;
|
|
return null;
|
|
}
|
|
|
|
$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);
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
}
|