mirror of
https://github.com/torrentpier/torrentpier-lts.git
synced 2025-03-01 15:21:02 +03:00
548 lines
16 KiB
PHP
548 lines
16 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 Memcached as MemcachedResource;
|
|
use ReflectionClass;
|
|
use Traversable;
|
|
use Zend\Cache\Exception;
|
|
use Zend\Stdlib\ArrayUtils;
|
|
|
|
/**
|
|
* This is a resource manager for memcached
|
|
*/
|
|
class MemcachedResourceManager
|
|
{
|
|
/**
|
|
* Registered resources
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $resources = array();
|
|
|
|
/**
|
|
* Get servers
|
|
* @param string $id
|
|
* @throws Exception\RuntimeException
|
|
* @return array array('host' => <host>, 'port' => <port>, 'weight' => <weight>)
|
|
*/
|
|
public function getServers($id)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
throw new Exception\RuntimeException("No resource with id '{$id}'");
|
|
}
|
|
|
|
$resource = & $this->resources[$id];
|
|
|
|
if ($resource instanceof MemcachedResource) {
|
|
return $resource->getServerList();
|
|
}
|
|
return $resource['servers'];
|
|
}
|
|
|
|
/**
|
|
* Normalize one server into the following format:
|
|
* array('host' => <host>, 'port' => <port>, 'weight' => <weight>)
|
|
*
|
|
* @param string|array &$server
|
|
* @throws Exception\InvalidArgumentException
|
|
*/
|
|
protected function normalizeServer(&$server)
|
|
{
|
|
$host = null;
|
|
$port = 11211;
|
|
$weight = 0;
|
|
|
|
// convert a single server into an array
|
|
if ($server instanceof Traversable) {
|
|
$server = ArrayUtils::iteratorToArray($server);
|
|
}
|
|
|
|
if (is_array($server)) {
|
|
// array(<host>[, <port>[, <weight>]])
|
|
if (isset($server[0])) {
|
|
$host = (string) $server[0];
|
|
$port = isset($server[1]) ? (int) $server[1] : $port;
|
|
$weight = isset($server[2]) ? (int) $server[2] : $weight;
|
|
}
|
|
|
|
// array('host' => <host>[, 'port' => <port>[, 'weight' => <weight>]])
|
|
if (!isset($server[0]) && isset($server['host'])) {
|
|
$host = (string) $server['host'];
|
|
$port = isset($server['port']) ? (int) $server['port'] : $port;
|
|
$weight = isset($server['weight']) ? (int) $server['weight'] : $weight;
|
|
}
|
|
} else {
|
|
// parse server from URI host{:?port}{?weight}
|
|
$server = trim($server);
|
|
if (strpos($server, '://') === false) {
|
|
$server = 'tcp://' . $server;
|
|
}
|
|
|
|
$server = parse_url($server);
|
|
if (!$server) {
|
|
throw new Exception\InvalidArgumentException("Invalid server given");
|
|
}
|
|
|
|
$host = $server['host'];
|
|
$port = isset($server['port']) ? (int) $server['port'] : $port;
|
|
|
|
if (isset($server['query'])) {
|
|
$query = null;
|
|
parse_str($server['query'], $query);
|
|
if (isset($query['weight'])) {
|
|
$weight = (int) $query['weight'];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!$host) {
|
|
throw new Exception\InvalidArgumentException('Missing required server host');
|
|
}
|
|
|
|
$server = array(
|
|
'host' => $host,
|
|
'port' => $port,
|
|
'weight' => $weight,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Check if a resource exists
|
|
*
|
|
* @param string $id
|
|
* @return bool
|
|
*/
|
|
public function hasResource($id)
|
|
{
|
|
return isset($this->resources[$id]);
|
|
}
|
|
|
|
/**
|
|
* Gets a memcached resource
|
|
*
|
|
* @param string $id
|
|
* @return MemcachedResource
|
|
* @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 instanceof MemcachedResource) {
|
|
return $resource;
|
|
}
|
|
|
|
if ($resource['persistent_id'] !== '') {
|
|
$memc = new MemcachedResource($resource['persistent_id']);
|
|
} else {
|
|
$memc = new MemcachedResource();
|
|
}
|
|
|
|
if (method_exists($memc, 'setOptions')) {
|
|
$memc->setOptions($resource['lib_options']);
|
|
} else {
|
|
foreach ($resource['lib_options'] as $k => $v) {
|
|
$memc->setOption($k, $v);
|
|
}
|
|
}
|
|
|
|
// merge and add servers (with persistence id servers could be added already)
|
|
$servers = array_udiff($resource['servers'], $memc->getServerList(), array($this, 'compareServers'));
|
|
if ($servers) {
|
|
$memc->addServers($servers);
|
|
}
|
|
|
|
// buffer and return
|
|
$this->resources[$id] = $memc;
|
|
return $memc;
|
|
}
|
|
|
|
/**
|
|
* Set a resource
|
|
*
|
|
* @param string $id
|
|
* @param array|Traversable|MemcachedResource $resource
|
|
* @return MemcachedResourceManager Fluent interface
|
|
*/
|
|
public function setResource($id, $resource)
|
|
{
|
|
$id = (string) $id;
|
|
|
|
if (!($resource instanceof MemcachedResource)) {
|
|
if ($resource instanceof Traversable) {
|
|
$resource = ArrayUtils::iteratorToArray($resource);
|
|
} elseif (!is_array($resource)) {
|
|
throw new Exception\InvalidArgumentException(
|
|
'Resource must be an instance of Memcached or an array or Traversable'
|
|
);
|
|
}
|
|
|
|
$resource = array_merge(array(
|
|
'persistent_id' => '',
|
|
'lib_options' => array(),
|
|
'servers' => array(),
|
|
), $resource);
|
|
|
|
// normalize and validate params
|
|
$this->normalizePersistentId($resource['persistent_id']);
|
|
$this->normalizeLibOptions($resource['lib_options']);
|
|
$this->normalizeServers($resource['servers']);
|
|
}
|
|
|
|
$this->resources[$id] = $resource;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Remove a resource
|
|
*
|
|
* @param string $id
|
|
* @return MemcachedResourceManager Fluent interface
|
|
*/
|
|
public function removeResource($id)
|
|
{
|
|
unset($this->resources[$id]);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set the persistent id
|
|
*
|
|
* @param string $id
|
|
* @param string $persistentId
|
|
* @return MemcachedResourceManager 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 MemcachedResource) {
|
|
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 MemcachedResource) {
|
|
throw new Exception\RuntimeException(
|
|
"Can't get persistent id of an instantiated memcached resource"
|
|
);
|
|
}
|
|
|
|
return $resource['persistent_id'];
|
|
}
|
|
|
|
/**
|
|
* Normalize the persistent id
|
|
*
|
|
* @param string $persistentId
|
|
*/
|
|
protected function normalizePersistentId(& $persistentId)
|
|
{
|
|
$persistentId = (string) $persistentId;
|
|
}
|
|
|
|
/**
|
|
* Set Libmemcached options
|
|
*
|
|
* @param string $id
|
|
* @param array $libOptions
|
|
* @return MemcachedResourceManager 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];
|
|
if ($resource instanceof MemcachedResource) {
|
|
if (method_exists($resource, 'setOptions')) {
|
|
$resource->setOptions($libOptions);
|
|
} else {
|
|
foreach ($libOptions as $key => $value) {
|
|
$resource->setOption($key, $value);
|
|
}
|
|
}
|
|
} else {
|
|
$resource['lib_options'] = $libOptions;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get Libmemcached 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 MemcachedResource) {
|
|
$libOptions = array();
|
|
$reflection = new ReflectionClass('Memcached');
|
|
$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 Libmemcached option
|
|
*
|
|
* @param string $id
|
|
* @param string|int $key
|
|
* @param mixed $value
|
|
* @return MemcachedResourceManager Fluent interface
|
|
*/
|
|
public function setLibOption($id, $key, $value)
|
|
{
|
|
return $this->setLibOptions($id, array($key => $value));
|
|
}
|
|
|
|
/**
|
|
* Get one Libmemcached 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 MemcachedResource) {
|
|
return $resource->getOption($key);
|
|
}
|
|
|
|
return isset($resource['lib_options'][$key]) ? $resource['lib_options'][$key] : null;
|
|
}
|
|
|
|
/**
|
|
* Normalize libmemcached 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 = 'Memcached::OPT_' . str_replace(array(' ', '-'), '_', strtoupper($key));
|
|
if (!defined($const)) {
|
|
throw new Exception\InvalidArgumentException("Unknown libmemcached option '{$key}' ({$const})");
|
|
}
|
|
$key = constant($const);
|
|
} else {
|
|
$key = (int) $key;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set servers
|
|
*
|
|
* $servers can be an array list or a comma separated list of servers.
|
|
* One server in the list can be descripted as follows:
|
|
* - URI: [tcp://]<host>[:<port>][?weight=<weight>]
|
|
* - Assoc: array('host' => <host>[, 'port' => <port>][, 'weight' => <weight>])
|
|
* - List: array(<host>[, <port>][, <weight>])
|
|
*
|
|
* @param string $id
|
|
* @param string|array $servers
|
|
* @return MemcachedResourceManager
|
|
*/
|
|
public function setServers($id, $servers)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
return $this->setResource($id, array(
|
|
'servers' => $servers
|
|
));
|
|
}
|
|
|
|
$this->normalizeServers($servers);
|
|
|
|
$resource = & $this->resources[$id];
|
|
if ($resource instanceof MemcachedResource) {
|
|
// don't add servers twice
|
|
$servers = array_udiff($servers, $resource->getServerList(), array($this, 'compareServers'));
|
|
if ($servers) {
|
|
$resource->addServers($servers);
|
|
}
|
|
} else {
|
|
$resource['servers'] = $servers;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add servers
|
|
*
|
|
* @param string $id
|
|
* @param string|array $servers
|
|
* @return MemcachedResourceManager
|
|
*/
|
|
public function addServers($id, $servers)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
return $this->setResource($id, array(
|
|
'servers' => $servers
|
|
));
|
|
}
|
|
|
|
$this->normalizeServers($servers);
|
|
|
|
$resource = & $this->resources[$id];
|
|
if ($resource instanceof MemcachedResource) {
|
|
// don't add servers twice
|
|
$servers = array_udiff($servers, $resource->getServerList(), array($this, 'compareServers'));
|
|
if ($servers) {
|
|
$resource->addServers($servers);
|
|
}
|
|
} else {
|
|
// don't add servers twice
|
|
$resource['servers'] = array_merge(
|
|
$resource['servers'],
|
|
array_udiff($servers, $resource['servers'], array($this, 'compareServers'))
|
|
);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add one server
|
|
*
|
|
* @param string $id
|
|
* @param string|array $server
|
|
* @return MemcachedResourceManager
|
|
*/
|
|
public function addServer($id, $server)
|
|
{
|
|
return $this->addServers($id, array($server));
|
|
}
|
|
|
|
/**
|
|
* Normalize a list of servers into the following format:
|
|
* array(array('host' => <host>, 'port' => <port>, 'weight' => <weight>)[, ...])
|
|
*
|
|
* @param string|array $servers
|
|
*/
|
|
protected function normalizeServers(& $servers)
|
|
{
|
|
if (!is_array($servers) && !$servers instanceof Traversable) {
|
|
// Convert string into a list of servers
|
|
$servers = explode(',', $servers);
|
|
}
|
|
|
|
$result = array();
|
|
foreach ($servers as $server) {
|
|
$this->normalizeServer($server);
|
|
$result[$server['host'] . ':' . $server['port']] = $server;
|
|
}
|
|
|
|
$servers = array_values($result);
|
|
}
|
|
|
|
/**
|
|
* Compare 2 normalized server arrays
|
|
* (Compares only the host and the port)
|
|
*
|
|
* @param array $serverA
|
|
* @param array $serverB
|
|
* @return int
|
|
*/
|
|
protected function compareServers(array $serverA, array $serverB)
|
|
{
|
|
$keyA = $serverA['host'] . ':' . $serverA['port'];
|
|
$keyB = $serverB['host'] . ':' . $serverB['port'];
|
|
if ($keyA === $keyB) {
|
|
return 0;
|
|
}
|
|
return $keyA > $keyB ? 1 : -1;
|
|
}
|
|
}
|