mirror of
https://github.com/torrentpier/torrentpier-lts.git
synced 2025-03-01 15:21:02 +03:00
646 lines
18 KiB
PHP
646 lines
18 KiB
PHP
<?php
|
|
/**
|
|
* Zend Framework (http://framework.zend.com/)
|
|
*
|
|
* @link http://github.com/zendframework/zf2 for the canonical source repository
|
|
* @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 ReflectionClass;
|
|
use Traversable;
|
|
use Zend\Cache\Exception;
|
|
use Zend\Stdlib\ArrayUtils;
|
|
|
|
/**
|
|
* This is a resource manager for redis
|
|
*/
|
|
class RedisResourceManager
|
|
{
|
|
/**
|
|
* Registered resources
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $resources = array();
|
|
|
|
/**
|
|
* Check if a resource exists
|
|
*
|
|
* @param string $id
|
|
* @return bool
|
|
*/
|
|
public function hasResource($id)
|
|
{
|
|
return isset($this->resources[$id]);
|
|
}
|
|
|
|
/**
|
|
* Get redis server version
|
|
*
|
|
* @param string $id
|
|
* @return int
|
|
* @throws Exception\RuntimeException
|
|
*/
|
|
public function getMajorVersion($id)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
throw new Exception\RuntimeException("No resource with id '{$id}'");
|
|
}
|
|
|
|
$resource = & $this->resources[$id];
|
|
return (int) $resource['version'];
|
|
}
|
|
|
|
/**
|
|
* Get redis server version
|
|
*
|
|
* @deprecated 2.2.2 Use getMajorVersion instead
|
|
*
|
|
* @param string $id
|
|
* @return int
|
|
* @throws Exception\RuntimeException
|
|
*/
|
|
public function getMayorVersion($id)
|
|
{
|
|
return $this->getMajorVersion($id);
|
|
}
|
|
|
|
/**
|
|
* Get redis resource database
|
|
*
|
|
* @param string $id
|
|
* @return string
|
|
*/
|
|
public function getDatabase($id)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
throw new Exception\RuntimeException("No resource with id '{$id}'");
|
|
}
|
|
|
|
$resource = & $this->resources[$id];
|
|
return $resource['database'];
|
|
}
|
|
|
|
/**
|
|
* Get redis resource password
|
|
*
|
|
* @param string $id
|
|
* @return string
|
|
*/
|
|
public function getPassword($id)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
throw new Exception\RuntimeException("No resource with id '{$id}'");
|
|
}
|
|
|
|
$resource = & $this->resources[$id];
|
|
return $resource['password'];
|
|
}
|
|
|
|
/**
|
|
* Gets a redis resource
|
|
*
|
|
* @param string $id
|
|
* @return RedisResourceManager
|
|
* @throws Exception\RuntimeException
|
|
*/
|
|
public function getResource($id)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
throw new Exception\RuntimeException("No resource with id '{$id}'");
|
|
}
|
|
|
|
$resource = & $this->resources[$id];
|
|
if ($resource['resource'] instanceof RedisResource) {
|
|
//in case new server was set then connect
|
|
if (!$resource['initialized']) {
|
|
$this->connect($resource);
|
|
}
|
|
$info = $resource['resource']->info();
|
|
$resource['version'] = $info['redis_version'];
|
|
return $resource['resource'];
|
|
}
|
|
|
|
$redis = new RedisResource();
|
|
|
|
$resource['resource'] = $redis;
|
|
$this->connect($resource);
|
|
|
|
foreach ($resource['lib_options'] as $k => $v) {
|
|
$redis->setOption($k, $v);
|
|
}
|
|
|
|
$info = $redis->info();
|
|
$resource['version'] = $info['redis_version'];
|
|
$this->resources[$id]['resource'] = $redis;
|
|
return $redis;
|
|
}
|
|
|
|
/**
|
|
* Get server
|
|
* @param string $id
|
|
* @throws Exception\RuntimeException
|
|
* @return array array('host' => <host>[, 'port' => <port>[, 'timeout' => <timeout>]])
|
|
*/
|
|
public function getServer($id)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
throw new Exception\RuntimeException("No resource with id '{$id}'");
|
|
}
|
|
|
|
$resource = & $this->resources[$id];
|
|
return $resource['server'];
|
|
}
|
|
|
|
/**
|
|
* Normalize one server into the following format:
|
|
* array('host' => <host>[, 'port' => <port>[, 'timeout' => <timeout>]])
|
|
*
|
|
* @param string|array $server
|
|
*
|
|
* @throws Exception\InvalidArgumentException
|
|
*/
|
|
protected function normalizeServer(&$server)
|
|
{
|
|
$host = null;
|
|
$port = null;
|
|
$timeout = 0;
|
|
|
|
// convert a single server into an array
|
|
if ($server instanceof Traversable) {
|
|
$server = ArrayUtils::iteratorToArray($server);
|
|
}
|
|
|
|
if (is_array($server)) {
|
|
// array(<host>[, <port>[, <timeout>]])
|
|
if (isset($server[0])) {
|
|
$host = (string) $server[0];
|
|
$port = isset($server[1]) ? (int) $server[1] : $port;
|
|
$timeout = isset($server[2]) ? (int) $server[2] : $timeout;
|
|
}
|
|
|
|
// array('host' => <host>[, 'port' => <port>, ['timeout' => <timeout>]])
|
|
if (!isset($server[0]) && isset($server['host'])) {
|
|
$host = (string) $server['host'];
|
|
$port = isset($server['port']) ? (int) $server['port'] : $port;
|
|
$timeout = isset($server['timeout']) ? (int) $server['timeout'] : $timeout;
|
|
}
|
|
} else {
|
|
// parse server from URI host{:?port}
|
|
$server = trim($server);
|
|
if (strpos($server, '/') !== 0) {
|
|
//non unix domain socket connection
|
|
$server = parse_url($server);
|
|
} else {
|
|
$server = array('host' => $server);
|
|
}
|
|
if (!$server) {
|
|
throw new Exception\InvalidArgumentException("Invalid server given");
|
|
}
|
|
|
|
$host = $server['host'];
|
|
$port = isset($server['port']) ? (int) $server['port'] : $port;
|
|
$timeout = isset($server['timeout']) ? (int) $server['timeout'] : $timeout;
|
|
}
|
|
|
|
if (!$host) {
|
|
throw new Exception\InvalidArgumentException('Missing required server host');
|
|
}
|
|
|
|
$server = array(
|
|
'host' => $host,
|
|
'port' => $port,
|
|
'timeout' => $timeout,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Extract password to be used on connection
|
|
*
|
|
* @param mixed $resource
|
|
* @param mixed $serverUri
|
|
*
|
|
* @return string|null
|
|
*/
|
|
protected function extractPassword($resource, $serverUri)
|
|
{
|
|
if (! empty($resource['password'])) {
|
|
return $resource['password'];
|
|
}
|
|
|
|
if (! is_string($serverUri)) {
|
|
return;
|
|
}
|
|
|
|
// parse server from URI host{:?port}
|
|
$server = trim($serverUri);
|
|
|
|
if (strpos($server, '/') === 0) {
|
|
return;
|
|
}
|
|
|
|
//non unix domain socket connection
|
|
$server = parse_url($server);
|
|
|
|
return isset($server['pass']) ? $server['pass'] : null;
|
|
}
|
|
|
|
/**
|
|
* Connects to redis server
|
|
*
|
|
*
|
|
* @param array & $resource
|
|
*
|
|
* @return null
|
|
* @throws Exception\RuntimeException
|
|
*/
|
|
protected function connect(array & $resource)
|
|
{
|
|
$server = $resource['server'];
|
|
$redis = $resource['resource'];
|
|
if ($resource['persistent_id'] !== '') {
|
|
//connect or reuse persistent connection
|
|
$success = $redis->pconnect($server['host'], $server['port'], $server['timeout'], $server['persistent_id']);
|
|
} elseif ($server['port']) {
|
|
$success = $redis->connect($server['host'], $server['port'], $server['timeout']);
|
|
} elseif ($server['timeout']) {
|
|
//connect through unix domain socket
|
|
$success = $redis->connect($server['host'], $server['timeout']);
|
|
} else {
|
|
$success = $redis->connect($server['host']);
|
|
}
|
|
|
|
if (!$success) {
|
|
throw new Exception\RuntimeException('Could not estabilish connection with Redis instance');
|
|
}
|
|
|
|
$resource['initialized'] = true;
|
|
if ($resource['password']) {
|
|
$redis->auth($resource['password']);
|
|
}
|
|
$redis->select($resource['database']);
|
|
}
|
|
|
|
/**
|
|
* Set a resource
|
|
*
|
|
* @param string $id
|
|
* @param array|Traversable|RedisResource $resource
|
|
* @return RedisResourceManager Fluent interface
|
|
*/
|
|
public function setResource($id, $resource)
|
|
{
|
|
$id = (string) $id;
|
|
//TODO: how to get back redis connection info from resource?
|
|
$defaults = array(
|
|
'persistent_id' => '',
|
|
'lib_options' => array(),
|
|
'server' => array(),
|
|
'password' => '',
|
|
'database' => 0,
|
|
'resource' => null,
|
|
'initialized' => false,
|
|
'version' => 0,
|
|
);
|
|
if (!$resource instanceof RedisResource) {
|
|
if ($resource instanceof Traversable) {
|
|
$resource = ArrayUtils::iteratorToArray($resource);
|
|
} elseif (!is_array($resource)) {
|
|
throw new Exception\InvalidArgumentException(
|
|
'Resource must be an instance of an array or Traversable'
|
|
);
|
|
}
|
|
|
|
$resource = array_merge($defaults, $resource);
|
|
// normalize and validate params
|
|
$this->normalizePersistentId($resource['persistent_id']);
|
|
$this->normalizeLibOptions($resource['lib_options']);
|
|
|
|
// #6495 note: order is important here, as `normalizeServer` applies destructive
|
|
// transformations on $resource['server']
|
|
$resource['password'] = $this->extractPassword($resource, $resource['server']);
|
|
|
|
$this->normalizeServer($resource['server']);
|
|
} else {
|
|
//there are two ways of determining if redis is already initialized
|
|
//with connect function:
|
|
//1) pinging server
|
|
//2) checking undocumented property socket which is available only
|
|
//after successful connect
|
|
$resource = array_merge(
|
|
$defaults,
|
|
array(
|
|
'resource' => $resource,
|
|
'initialized' => isset($resource->socket),
|
|
)
|
|
);
|
|
}
|
|
$this->resources[$id] = $resource;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Remove a resource
|
|
*
|
|
* @param string $id
|
|
* @return RedisResourceManager Fluent interface
|
|
*/
|
|
public function removeResource($id)
|
|
{
|
|
unset($this->resources[$id]);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set the persistent id
|
|
*
|
|
* @param string $id
|
|
* @param string $persistentId
|
|
* @return RedisResourceManager Fluent interface
|
|
* @throws Exception\RuntimeException
|
|
*/
|
|
public function setPersistentId($id, $persistentId)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
return $this->setResource($id, array(
|
|
'persistent_id' => $persistentId
|
|
));
|
|
}
|
|
|
|
$resource = & $this->resources[$id];
|
|
if ($resource instanceof RedisResource) {
|
|
throw new Exception\RuntimeException(
|
|
"Can't change persistent id of resource {$id} after instanziation"
|
|
);
|
|
}
|
|
|
|
$this->normalizePersistentId($persistentId);
|
|
$resource['persistent_id'] = $persistentId;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get the persistent id
|
|
*
|
|
* @param string $id
|
|
* @return string
|
|
* @throws Exception\RuntimeException
|
|
*/
|
|
public function getPersistentId($id)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
throw new Exception\RuntimeException("No resource with id '{$id}'");
|
|
}
|
|
|
|
$resource = & $this->resources[$id];
|
|
|
|
if ($resource instanceof RedisResource) {
|
|
throw new Exception\RuntimeException(
|
|
"Can't get persistent id of an instantiated redis resource"
|
|
);
|
|
}
|
|
|
|
return $resource['persistent_id'];
|
|
}
|
|
|
|
/**
|
|
* Normalize the persistent id
|
|
*
|
|
* @param string $persistentId
|
|
*/
|
|
protected function normalizePersistentId(& $persistentId)
|
|
{
|
|
$persistentId = (string) $persistentId;
|
|
}
|
|
|
|
/**
|
|
* Set Redis options
|
|
*
|
|
* @param string $id
|
|
* @param array $libOptions
|
|
* @return RedisResourceManager Fluent interface
|
|
*/
|
|
public function setLibOptions($id, array $libOptions)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
return $this->setResource($id, array(
|
|
'lib_options' => $libOptions
|
|
));
|
|
}
|
|
|
|
$this->normalizeLibOptions($libOptions);
|
|
$resource = & $this->resources[$id];
|
|
|
|
$resource['lib_options'] = $libOptions;
|
|
|
|
if ($resource['resource'] instanceof RedisResource) {
|
|
$redis = & $resource['resource'];
|
|
if (method_exists($redis, 'setOptions')) {
|
|
$redis->setOptions($libOptions);
|
|
} else {
|
|
foreach ($libOptions as $key => $value) {
|
|
$redis->setOption($key, $value);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get Redis options
|
|
*
|
|
* @param string $id
|
|
* @return array
|
|
* @throws Exception\RuntimeException
|
|
*/
|
|
public function getLibOptions($id)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
throw new Exception\RuntimeException("No resource with id '{$id}'");
|
|
}
|
|
|
|
$resource = & $this->resources[$id];
|
|
|
|
if ($resource instanceof RedisResource) {
|
|
$libOptions = array();
|
|
$reflection = new ReflectionClass('Redis');
|
|
$constants = $reflection->getConstants();
|
|
foreach ($constants as $constName => $constValue) {
|
|
if (substr($constName, 0, 4) == 'OPT_') {
|
|
$libOptions[$constValue] = $resource->getOption($constValue);
|
|
}
|
|
}
|
|
return $libOptions;
|
|
}
|
|
return $resource['lib_options'];
|
|
}
|
|
|
|
/**
|
|
* Set one Redis option
|
|
*
|
|
* @param string $id
|
|
* @param string|int $key
|
|
* @param mixed $value
|
|
* @return RedisResourceManager Fluent interface
|
|
*/
|
|
public function setLibOption($id, $key, $value)
|
|
{
|
|
return $this->setLibOptions($id, array($key => $value));
|
|
}
|
|
|
|
/**
|
|
* Get one Redis option
|
|
*
|
|
* @param string $id
|
|
* @param string|int $key
|
|
* @return mixed
|
|
* @throws Exception\RuntimeException
|
|
*/
|
|
public function getLibOption($id, $key)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
throw new Exception\RuntimeException("No resource with id '{$id}'");
|
|
}
|
|
|
|
$this->normalizeLibOptionKey($key);
|
|
$resource = & $this->resources[$id];
|
|
|
|
if ($resource instanceof RedisResource) {
|
|
return $resource->getOption($key);
|
|
}
|
|
|
|
return isset($resource['lib_options'][$key]) ? $resource['lib_options'][$key] : null;
|
|
}
|
|
|
|
/**
|
|
* Normalize Redis options
|
|
*
|
|
* @param array|Traversable $libOptions
|
|
* @throws Exception\InvalidArgumentException
|
|
*/
|
|
protected function normalizeLibOptions(& $libOptions)
|
|
{
|
|
if (!is_array($libOptions) && !($libOptions instanceof Traversable)) {
|
|
throw new Exception\InvalidArgumentException(
|
|
"Lib-Options must be an array or an instance of Traversable"
|
|
);
|
|
}
|
|
|
|
$result = array();
|
|
foreach ($libOptions as $key => $value) {
|
|
$this->normalizeLibOptionKey($key);
|
|
$result[$key] = $value;
|
|
}
|
|
|
|
$libOptions = $result;
|
|
}
|
|
|
|
/**
|
|
* Convert option name into it's constant value
|
|
*
|
|
* @param string|int $key
|
|
* @throws Exception\InvalidArgumentException
|
|
*/
|
|
protected function normalizeLibOptionKey(& $key)
|
|
{
|
|
// convert option name into it's constant value
|
|
if (is_string($key)) {
|
|
$const = 'Redis::OPT_' . str_replace(array(' ', '-'), '_', strtoupper($key));
|
|
if (!defined($const)) {
|
|
throw new Exception\InvalidArgumentException("Unknown redis option '{$key}' ({$const})");
|
|
}
|
|
$key = constant($const);
|
|
} else {
|
|
$key = (int) $key;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set server
|
|
*
|
|
* Server can be described as follows:
|
|
* - URI: /path/to/sock.sock
|
|
* - Assoc: array('host' => <host>[, 'port' => <port>[, 'timeout' => <timeout>]])
|
|
* - List: array(<host>[, <port>, [, <timeout>]])
|
|
*
|
|
* @param string $id
|
|
* @param string|array $server
|
|
* @return RedisResourceManager
|
|
*/
|
|
public function setServer($id, $server)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
return $this->setResource($id, array(
|
|
'server' => $server
|
|
));
|
|
}
|
|
|
|
$this->normalizeServer($server);
|
|
|
|
$resource = & $this->resources[$id];
|
|
$resource['password'] = $this->extractPassword($resource, $server);
|
|
|
|
if ($resource['resource'] instanceof RedisResource) {
|
|
$resourceParams = array('server' => $server);
|
|
|
|
if (! empty($resource['password'])) {
|
|
$resourceParams['password'] = $resource['password'];
|
|
}
|
|
|
|
$this->setResource($id, $resourceParams);
|
|
} else {
|
|
$resource['server'] = $server;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set redis password
|
|
*
|
|
* @param string $id
|
|
* @param string $password
|
|
* @return RedisResource
|
|
*/
|
|
public function setPassword($id, $password)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
return $this->setResource($id, array(
|
|
'password' => $password,
|
|
));
|
|
}
|
|
|
|
$resource = & $this->resources[$id];
|
|
$resource['password'] = $password;
|
|
$resource['initialized'] = false;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set redis database number
|
|
*
|
|
* @param string $id
|
|
* @param int $database
|
|
* @return RedisResourceManager
|
|
*/
|
|
public function setDatabase($id, $database)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
return $this->setResource($id, array(
|
|
'database' => (int) $database,
|
|
));
|
|
}
|
|
|
|
$resource = & $this->resources[$id];
|
|
$resource['database'] = $database;
|
|
$resource['initialized'] = false;
|
|
return $this;
|
|
}
|
|
}
|