Source of file MemcacheResourceManager.php
Size: 19,120 Bytes - Last Modified: 2014-03-12T23:21:18+01:00
/home/theseer/Downloads/ZendFramework-2.3.0/library/Zend/Cache/Storage/Adapter/MemcacheResourceManager.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629 | <?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 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 * @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 array $libOptions */ 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|null * @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; } } |