mirror of
https://github.com/torrentpier/torrentpier-lts.git
synced 2025-02-28 15:10:54 +03:00
650 lines
19 KiB
PHP
650 lines
19 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 ArrayAccess;
|
|
use Memcache as MemcacheResource;
|
|
use Traversable;
|
|
use Zend\Cache\Exception;
|
|
use Zend\Stdlib\ArrayUtils;
|
|
|
|
/**
|
|
* This is a resource manager for memcache
|
|
*/
|
|
class MemcacheResourceManager
|
|
{
|
|
/**
|
|
* Registered resources
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $resources = array();
|
|
|
|
/**
|
|
* Default server values per resource
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $serverDefaults = array();
|
|
|
|
/**
|
|
* Failure callback per resource
|
|
*
|
|
* @var callable[]
|
|
*/
|
|
protected $failureCallbacks = array();
|
|
|
|
/**
|
|
* Check if a resource exists
|
|
*
|
|
* @param string $id
|
|
* @return bool
|
|
*/
|
|
public function hasResource($id)
|
|
{
|
|
return isset($this->resources[$id]);
|
|
}
|
|
|
|
/**
|
|
* Gets a memcache resource
|
|
*
|
|
* @param string $id
|
|
* @return MemcacheResource
|
|
* @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 MemcacheResource) {
|
|
return $resource;
|
|
}
|
|
|
|
$memc = new MemcacheResource();
|
|
$this->setResourceAutoCompressThreshold(
|
|
$memc,
|
|
$resource['auto_compress_threshold'],
|
|
$resource['auto_compress_min_savings']
|
|
);
|
|
foreach ($resource['servers'] as $server) {
|
|
$this->addServerToResource(
|
|
$memc,
|
|
$server,
|
|
$this->serverDefaults[$id],
|
|
$this->failureCallbacks[$id]
|
|
);
|
|
}
|
|
|
|
// buffer and return
|
|
$this->resources[$id] = $memc;
|
|
return $memc;
|
|
}
|
|
|
|
/**
|
|
* Set a resource
|
|
*
|
|
* @param string $id
|
|
* @param array|Traversable|MemcacheResource $resource
|
|
* @param callable $failureCallback
|
|
* @param array|Traversable $serverDefaults
|
|
* @return MemcacheResourceManager
|
|
*/
|
|
public function setResource($id, $resource, $failureCallback = null, $serverDefaults = array())
|
|
{
|
|
$id = (string) $id;
|
|
|
|
if ($serverDefaults instanceof Traversable) {
|
|
$serverDefaults = ArrayUtils::iteratorToArray($serverDefaults);
|
|
} elseif (!is_array($serverDefaults)) {
|
|
throw new Exception\InvalidArgumentException(
|
|
'ServerDefaults must be an instance Traversable or an array'
|
|
);
|
|
}
|
|
|
|
if (!($resource instanceof MemcacheResource)) {
|
|
if ($resource instanceof Traversable) {
|
|
$resource = ArrayUtils::iteratorToArray($resource);
|
|
} elseif (!is_array($resource)) {
|
|
throw new Exception\InvalidArgumentException(
|
|
'Resource must be an instance of Memcache or an array or Traversable'
|
|
);
|
|
}
|
|
|
|
if (isset($resource['server_defaults'])) {
|
|
$serverDefaults = array_merge($serverDefaults, $resource['server_defaults']);
|
|
unset($resource['server_defaults']);
|
|
}
|
|
|
|
$resourceOptions = array(
|
|
'servers' => array(),
|
|
'auto_compress_threshold' => null,
|
|
'auto_compress_min_savings' => null,
|
|
);
|
|
$resource = array_merge($resourceOptions, $resource);
|
|
|
|
// normalize and validate params
|
|
$this->normalizeAutoCompressThreshold(
|
|
$resource['auto_compress_threshold'],
|
|
$resource['auto_compress_min_savings']
|
|
);
|
|
$this->normalizeServers($resource['servers']);
|
|
}
|
|
|
|
$this->normalizeServerDefaults($serverDefaults);
|
|
|
|
$this->resources[$id] = $resource;
|
|
$this->failureCallbacks[$id] = $failureCallback;
|
|
$this->serverDefaults[$id] = $serverDefaults;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Remove a resource
|
|
*
|
|
* @param string $id
|
|
* @return MemcacheResourceManager
|
|
*/
|
|
public function removeResource($id)
|
|
{
|
|
unset($this->resources[$id]);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Normalize compress threshold options
|
|
*
|
|
* @param int|string|array|ArrayAccess $threshold
|
|
* @param float|string $minSavings
|
|
*/
|
|
protected function normalizeAutoCompressThreshold(& $threshold, & $minSavings)
|
|
{
|
|
if (is_array($threshold) || ($threshold instanceof ArrayAccess)) {
|
|
$tmpThreshold = (isset($threshold['threshold'])) ? $threshold['threshold'] : null;
|
|
$minSavings = (isset($threshold['min_savings'])) ? $threshold['min_savings'] : $minSavings;
|
|
$threshold = $tmpThreshold;
|
|
}
|
|
if (isset($threshold)) {
|
|
$threshold = (int) $threshold;
|
|
}
|
|
if (isset($minSavings)) {
|
|
$minSavings = (float) $minSavings;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set compress threshold on a Memcache resource
|
|
*
|
|
* @param MemcacheResource $resource
|
|
* @param int $threshold
|
|
* @param float $minSavings
|
|
*/
|
|
protected function setResourceAutoCompressThreshold(MemcacheResource $resource, $threshold, $minSavings)
|
|
{
|
|
if (!isset($threshold)) {
|
|
return;
|
|
}
|
|
if (isset($minSavings)) {
|
|
$resource->setCompressThreshold($threshold, $minSavings);
|
|
} else {
|
|
$resource->setCompressThreshold($threshold);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get compress threshold
|
|
*
|
|
* @param string $id
|
|
* @return int|null
|
|
* @throws \Zend\Cache\Exception\RuntimeException
|
|
*/
|
|
public function getAutoCompressThreshold($id)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
throw new Exception\RuntimeException("No resource with id '{$id}'");
|
|
}
|
|
|
|
$resource = & $this->resources[$id];
|
|
if ($resource instanceof MemcacheResource) {
|
|
// Cannot get options from Memcache resource once created
|
|
throw new Exception\RuntimeException("Cannot get compress threshold once resource is created");
|
|
}
|
|
return $resource['auto_compress_threshold'];
|
|
}
|
|
|
|
/**
|
|
* Set compress threshold
|
|
*
|
|
* @param string $id
|
|
* @param int|string|array|ArrayAccess|null $threshold
|
|
* @param float|string|bool $minSavings
|
|
* @return MemcacheResourceManager
|
|
*/
|
|
public function setAutoCompressThreshold($id, $threshold, $minSavings = false)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
return $this->setResource($id, array(
|
|
'auto_compress_threshold' => $threshold,
|
|
));
|
|
}
|
|
|
|
$this->normalizeAutoCompressThreshold($threshold, $minSavings);
|
|
|
|
$resource = & $this->resources[$id];
|
|
if ($resource instanceof MemcacheResource) {
|
|
$this->setResourceAutoCompressThreshold($resource, $threshold, $minSavings);
|
|
} else {
|
|
$resource['auto_compress_threshold'] = $threshold;
|
|
if ($minSavings !== false) {
|
|
$resource['auto_compress_min_savings'] = $minSavings;
|
|
}
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get compress min savings
|
|
*
|
|
* @param string $id
|
|
* @return float|null
|
|
* @throws Exception\RuntimeException
|
|
*/
|
|
public function getAutoCompressMinSavings($id)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
throw new Exception\RuntimeException("No resource with id '{$id}'");
|
|
}
|
|
|
|
$resource = & $this->resources[$id];
|
|
if ($resource instanceof MemcacheResource) {
|
|
// Cannot get options from Memcache resource once created
|
|
throw new Exception\RuntimeException("Cannot get compress min savings once resource is created");
|
|
}
|
|
return $resource['auto_compress_min_savings'];
|
|
}
|
|
|
|
/**
|
|
* Set compress min savings
|
|
*
|
|
* @param string $id
|
|
* @param float|string|null $minSavings
|
|
* @return MemcacheResourceManager
|
|
* @throws \Zend\Cache\Exception\RuntimeException
|
|
*/
|
|
public function setAutoCompressMinSavings($id, $minSavings)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
return $this->setResource($id, array(
|
|
'auto_compress_min_savings' => $minSavings,
|
|
));
|
|
}
|
|
|
|
$minSavings = (float) $minSavings;
|
|
|
|
$resource = & $this->resources[$id];
|
|
if ($resource instanceof MemcacheResource) {
|
|
throw new Exception\RuntimeException(
|
|
"Cannot set compress min savings without a threshold value once a resource is created"
|
|
);
|
|
} else {
|
|
$resource['auto_compress_min_savings'] = $minSavings;
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set default server values
|
|
* array(
|
|
* 'persistent' => <persistent>, 'weight' => <weight>,
|
|
* 'timeout' => <timeout>, 'retry_interval' => <retryInterval>,
|
|
* )
|
|
* @param string $id
|
|
* @param array $serverDefaults
|
|
* @return MemcacheResourceManager
|
|
*/
|
|
public function setServerDefaults($id, array $serverDefaults)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
return $this->setResource($id, array(
|
|
'server_defaults' => $serverDefaults
|
|
));
|
|
}
|
|
|
|
$this->normalizeServerDefaults($serverDefaults);
|
|
$this->serverDefaults[$id] = $serverDefaults;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get default server values
|
|
*
|
|
* @param string $id
|
|
* @return array
|
|
* @throws Exception\RuntimeException
|
|
*/
|
|
public function getServerDefaults($id)
|
|
{
|
|
if (!isset($this->serverDefaults[$id])) {
|
|
throw new Exception\RuntimeException("No resource with id '{$id}'");
|
|
}
|
|
return $this->serverDefaults[$id];
|
|
}
|
|
|
|
/**
|
|
* @param array $serverDefaults
|
|
* @throws Exception\InvalidArgumentException
|
|
*/
|
|
protected function normalizeServerDefaults(& $serverDefaults)
|
|
{
|
|
if (!is_array($serverDefaults) && !($serverDefaults instanceof Traversable)) {
|
|
throw new Exception\InvalidArgumentException(
|
|
"Server defaults must be an array or an instance of Traversable"
|
|
);
|
|
}
|
|
|
|
// Defaults
|
|
$result = array(
|
|
'persistent' => true,
|
|
'weight' => 1,
|
|
'timeout' => 1, // seconds
|
|
'retry_interval' => 15, // seconds
|
|
);
|
|
|
|
foreach ($serverDefaults as $key => $value) {
|
|
switch ($key) {
|
|
case 'persistent':
|
|
$value = (bool) $value;
|
|
break;
|
|
case 'weight':
|
|
case 'timeout':
|
|
case 'retry_interval':
|
|
$value = (int) $value;
|
|
break;
|
|
}
|
|
$result[$key] = $value;
|
|
}
|
|
|
|
$serverDefaults = $result;
|
|
}
|
|
|
|
/**
|
|
* Set callback for server connection failures
|
|
*
|
|
* @param string $id
|
|
* @param callable|null $failureCallback
|
|
* @return MemcacheResourceManager
|
|
*/
|
|
public function setFailureCallback($id, $failureCallback)
|
|
{
|
|
if (!$this->hasResource($id)) {
|
|
return $this->setResource($id, array(), $failureCallback);
|
|
}
|
|
|
|
$this->failureCallbacks[$id] = $failureCallback;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get callback for server connection failures
|
|
*
|
|
* @param string $id
|
|
* @return callable
|
|
* @throws Exception\RuntimeException
|
|
*/
|
|
public function getFailureCallback($id)
|
|
{
|
|
if (!isset($this->failureCallbacks[$id])) {
|
|
throw new Exception\RuntimeException("No resource with id '{$id}'");
|
|
}
|
|
return $this->failureCallbacks[$id];
|
|
}
|
|
|
|
/**
|
|
* 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 MemcacheResource) {
|
|
throw new Exception\RuntimeException("Cannot get server list once resource is created");
|
|
}
|
|
return $resource['servers'];
|
|
}
|
|
|
|
/**
|
|
* Add servers
|
|
*
|
|
* @param string $id
|
|
* @param string|array $servers
|
|
* @return MemcacheResourceManager
|
|
*/
|
|
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 MemcacheResource) {
|
|
foreach ($servers as $server) {
|
|
$this->addServerToResource(
|
|
$resource,
|
|
$server,
|
|
$this->serverDefaults[$id],
|
|
$this->failureCallbacks[$id]
|
|
);
|
|
}
|
|
} 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 MemcacheResourceManager
|
|
*/
|
|
public function addServer($id, $server)
|
|
{
|
|
return $this->addServers($id, array($server));
|
|
}
|
|
|
|
/**
|
|
* @param MemcacheResource $resource
|
|
* @param array $server
|
|
* @param array $serverDefaults
|
|
* @param callable|null $failureCallback
|
|
*/
|
|
protected function addServerToResource(
|
|
MemcacheResource $resource,
|
|
array $server,
|
|
array $serverDefaults,
|
|
$failureCallback
|
|
) {
|
|
// Apply server defaults
|
|
$server = array_merge($serverDefaults, $server);
|
|
|
|
// Reorder parameters
|
|
$params = array(
|
|
$server['host'],
|
|
$server['port'],
|
|
$server['persistent'],
|
|
$server['weight'],
|
|
$server['timeout'],
|
|
$server['retry_interval'],
|
|
$server['status'],
|
|
);
|
|
if (isset($failureCallback)) {
|
|
$params[] = $failureCallback;
|
|
}
|
|
call_user_func_array(array($resource, 'addServer'), $params);
|
|
}
|
|
|
|
/**
|
|
* 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_string($servers)) {
|
|
// 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);
|
|
}
|
|
|
|
/**
|
|
* Normalize one server into the following format:
|
|
* array(
|
|
* 'host' => <host>, 'port' => <port>, 'weight' => <weight>,
|
|
* 'status' => <status>, 'persistent' => <persistent>,
|
|
* 'timeout' => <timeout>, 'retry_interval' => <retryInterval>,
|
|
* )
|
|
*
|
|
* @param string|array $server
|
|
* @throws Exception\InvalidArgumentException
|
|
*/
|
|
protected function normalizeServer(& $server)
|
|
{
|
|
// WARNING: The order of this array is important.
|
|
// Used for converting an ordered array to a keyed array.
|
|
// Append new options, do not insert or you will break BC.
|
|
$sTmp = array(
|
|
'host' => null,
|
|
'port' => 11211,
|
|
'weight' => null,
|
|
'status' => true,
|
|
'persistent' => null,
|
|
'timeout' => null,
|
|
'retry_interval' => null,
|
|
);
|
|
|
|
// convert a single server into an array
|
|
if ($server instanceof Traversable) {
|
|
$server = ArrayUtils::iteratorToArray($server);
|
|
}
|
|
|
|
if (is_array($server)) {
|
|
if (isset($server[0])) {
|
|
// Convert ordered array to keyed array
|
|
// array(<host>[, <port>[, <weight>[, <status>[, <persistent>[, <timeout>[, <retryInterval>]]]]]])
|
|
$server = array_combine(
|
|
array_slice(array_keys($sTmp), 0, count($server)),
|
|
$server
|
|
);
|
|
}
|
|
$sTmp = array_merge($sTmp, $server);
|
|
} elseif (is_string($server)) {
|
|
// parse server from URI host{:?port}{?weight}
|
|
$server = trim($server);
|
|
if (strpos($server, '://') === false) {
|
|
$server = 'tcp://' . $server;
|
|
}
|
|
|
|
$urlParts = parse_url($server);
|
|
if (!$urlParts) {
|
|
throw new Exception\InvalidArgumentException("Invalid server given");
|
|
}
|
|
|
|
$sTmp = array_merge($sTmp, array_intersect_key($urlParts, $sTmp));
|
|
if (isset($urlParts['query'])) {
|
|
$query = null;
|
|
parse_str($urlParts['query'], $query);
|
|
$sTmp = array_merge($sTmp, array_intersect_key($query, $sTmp));
|
|
}
|
|
}
|
|
|
|
if (!$sTmp['host']) {
|
|
throw new Exception\InvalidArgumentException('Missing required server host');
|
|
}
|
|
|
|
// Filter values
|
|
foreach ($sTmp as $key => $value) {
|
|
if (isset($value)) {
|
|
switch ($key) {
|
|
case 'host':
|
|
$value = (string) $value;
|
|
break;
|
|
case 'status':
|
|
case 'persistent':
|
|
$value = (bool) $value;
|
|
break;
|
|
case 'port':
|
|
case 'weight':
|
|
case 'timeout':
|
|
case 'retry_interval':
|
|
$value = (int) $value;
|
|
break;
|
|
}
|
|
}
|
|
$sTmp[$key] = $value;
|
|
}
|
|
$sTmp = array_filter(
|
|
$sTmp,
|
|
function ($val) {
|
|
return isset($val);
|
|
}
|
|
);
|
|
|
|
$server = $sTmp;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
}
|