composer package updates
[openemr.git] / vendor / zendframework / zend-cache / src / Storage / Adapter / MemcachedResourceManager.php
blob66611d98d556e0df66807b6edced592298d027aa
1 <?php
2 /**
3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
8 */
10 namespace Zend\Cache\Storage\Adapter;
12 use Memcached as MemcachedResource;
13 use ReflectionClass;
14 use Traversable;
15 use Zend\Cache\Exception;
16 use Zend\Stdlib\ArrayUtils;
18 /**
19 * This is a resource manager for memcached
21 class MemcachedResourceManager
23 /**
24 * Registered resources
26 * @var array
28 protected $resources = [];
30 /**
31 * Get servers
32 * @param string $id
33 * @throws Exception\RuntimeException
34 * @return array array('host' => <host>, 'port' => <port>, 'weight' => <weight>)
36 public function getServers($id)
38 if (! $this->hasResource($id)) {
39 throw new Exception\RuntimeException("No resource with id '{$id}'");
42 $resource = & $this->resources[$id];
44 if ($resource instanceof MemcachedResource) {
45 return $resource->getServerList();
47 return $resource['servers'];
50 /**
51 * Normalize one server into the following format:
52 * array('host' => <host>, 'port' => <port>, 'weight' => <weight>)
54 * @param string|array &$server
55 * @throws Exception\InvalidArgumentException
57 protected function normalizeServer(&$server)
59 $host = null;
60 $port = 11211;
61 $weight = 0;
63 // convert a single server into an array
64 if ($server instanceof Traversable) {
65 $server = ArrayUtils::iteratorToArray($server);
68 if (is_array($server)) {
69 // array(<host>[, <port>[, <weight>]])
70 if (isset($server[0])) {
71 $host = (string) $server[0];
72 $port = isset($server[1]) ? (int) $server[1] : $port;
73 $weight = isset($server[2]) ? (int) $server[2] : $weight;
76 // array('host' => <host>[, 'port' => <port>[, 'weight' => <weight>]])
77 if (! isset($server[0]) && isset($server['host'])) {
78 $host = (string) $server['host'];
79 $port = isset($server['port']) ? (int) $server['port'] : $port;
80 $weight = isset($server['weight']) ? (int) $server['weight'] : $weight;
82 } else {
83 // parse server from URI host{:?port}{?weight}
84 $server = trim($server);
85 if (strpos($server, '://') === false) {
86 $server = 'tcp://' . $server;
89 $server = parse_url($server);
90 if (! $server) {
91 throw new Exception\InvalidArgumentException("Invalid server given");
94 $host = $server['host'];
95 $port = isset($server['port']) ? (int) $server['port'] : $port;
97 if (isset($server['query'])) {
98 $query = null;
99 parse_str($server['query'], $query);
100 if (isset($query['weight'])) {
101 $weight = (int) $query['weight'];
106 if (! $host) {
107 throw new Exception\InvalidArgumentException('Missing required server host');
110 $server = [
111 'host' => $host,
112 'port' => $port,
113 'weight' => $weight,
118 * Check if a resource exists
120 * @param string $id
121 * @return bool
123 public function hasResource($id)
125 return isset($this->resources[$id]);
129 * Gets a memcached resource
131 * @param string $id
132 * @return MemcachedResource
133 * @throws Exception\RuntimeException
135 public function getResource($id)
137 if (! $this->hasResource($id)) {
138 throw new Exception\RuntimeException("No resource with id '{$id}'");
141 $resource = $this->resources[$id];
142 if ($resource instanceof MemcachedResource) {
143 return $resource;
146 if ($resource['persistent_id'] !== '') {
147 $memc = new MemcachedResource($resource['persistent_id']);
148 } else {
149 $memc = new MemcachedResource();
152 if (method_exists($memc, 'setOptions')) {
153 $memc->setOptions($resource['lib_options']);
154 } else {
155 foreach ($resource['lib_options'] as $k => $v) {
156 $memc->setOption($k, $v);
160 // merge and add servers (with persistence id servers could be added already)
161 $servers = array_udiff($resource['servers'], $memc->getServerList(), [$this, 'compareServers']);
162 if ($servers) {
163 $memc->addServers(array_values(array_map('array_values', $servers)));
166 // buffer and return
167 $this->resources[$id] = $memc;
168 return $memc;
172 * Set a resource
174 * @param string $id
175 * @param array|Traversable|MemcachedResource $resource
176 * @return MemcachedResourceManager Provides a fluent interface
178 public function setResource($id, $resource)
180 $id = (string) $id;
182 if (! ($resource instanceof MemcachedResource)) {
183 if ($resource instanceof Traversable) {
184 $resource = ArrayUtils::iteratorToArray($resource);
185 } elseif (! is_array($resource)) {
186 throw new Exception\InvalidArgumentException(
187 'Resource must be an instance of Memcached or an array or Traversable'
191 $resource = array_merge([
192 'persistent_id' => '',
193 'lib_options' => [],
194 'servers' => [],
195 ], $resource);
197 // normalize and validate params
198 $this->normalizePersistentId($resource['persistent_id']);
199 $this->normalizeLibOptions($resource['lib_options']);
200 $this->normalizeServers($resource['servers']);
203 $this->resources[$id] = $resource;
204 return $this;
208 * Remove a resource
210 * @param string $id
211 * @return MemcachedResourceManager Provides a fluent interface
213 public function removeResource($id)
215 unset($this->resources[$id]);
216 return $this;
220 * Set the persistent id
222 * @param string $id
223 * @param string $persistentId
224 * @return MemcachedResourceManager Provides a fluent interface
225 * @throws Exception\RuntimeException
227 public function setPersistentId($id, $persistentId)
229 if (! $this->hasResource($id)) {
230 return $this->setResource($id, [
231 'persistent_id' => $persistentId
235 $resource = & $this->resources[$id];
236 if ($resource instanceof MemcachedResource) {
237 throw new Exception\RuntimeException(
238 "Can't change persistent id of resource {$id} after instanziation"
242 $this->normalizePersistentId($persistentId);
243 $resource['persistent_id'] = $persistentId;
245 return $this;
249 * Get the persistent id
251 * @param string $id
252 * @return string
253 * @throws Exception\RuntimeException
255 public function getPersistentId($id)
257 if (! $this->hasResource($id)) {
258 throw new Exception\RuntimeException("No resource with id '{$id}'");
261 $resource = & $this->resources[$id];
263 if ($resource instanceof MemcachedResource) {
264 throw new Exception\RuntimeException(
265 "Can't get persistent id of an instantiated memcached resource"
269 return $resource['persistent_id'];
273 * Normalize the persistent id
275 * @param string $persistentId
277 protected function normalizePersistentId(& $persistentId)
279 $persistentId = (string) $persistentId;
283 * Set Libmemcached options
285 * @param string $id
286 * @param array $libOptions
287 * @return MemcachedResourceManager Provides a fluent interface
289 public function setLibOptions($id, array $libOptions)
291 if (! $this->hasResource($id)) {
292 return $this->setResource($id, [
293 'lib_options' => $libOptions
297 $this->normalizeLibOptions($libOptions);
299 $resource = & $this->resources[$id];
300 if ($resource instanceof MemcachedResource) {
301 if (method_exists($resource, 'setOptions')) {
302 $resource->setOptions($libOptions);
303 } else {
304 foreach ($libOptions as $key => $value) {
305 $resource->setOption($key, $value);
308 } else {
309 $resource['lib_options'] = $libOptions;
312 return $this;
316 * Get Libmemcached options
318 * @param string $id
319 * @return array
320 * @throws Exception\RuntimeException
322 public function getLibOptions($id)
324 if (! $this->hasResource($id)) {
325 throw new Exception\RuntimeException("No resource with id '{$id}'");
328 $resource = & $this->resources[$id];
330 if ($resource instanceof MemcachedResource) {
331 $libOptions = [];
332 $reflection = new ReflectionClass('Memcached');
333 $constants = $reflection->getConstants();
334 foreach ($constants as $constName => $constValue) {
335 if (substr($constName, 0, 4) == 'OPT_') {
336 $libOptions[$constValue] = $resource->getOption($constValue);
339 return $libOptions;
341 return $resource['lib_options'];
345 * Set one Libmemcached option
347 * @param string $id
348 * @param string|int $key
349 * @param mixed $value
350 * @return MemcachedResourceManager Fluent interface
352 public function setLibOption($id, $key, $value)
354 return $this->setLibOptions($id, [$key => $value]);
358 * Get one Libmemcached option
360 * @param string $id
361 * @param string|int $key
362 * @return mixed
363 * @throws Exception\RuntimeException
365 public function getLibOption($id, $key)
367 if (! $this->hasResource($id)) {
368 throw new Exception\RuntimeException("No resource with id '{$id}'");
371 $this->normalizeLibOptionKey($key);
372 $resource = & $this->resources[$id];
374 if ($resource instanceof MemcachedResource) {
375 return $resource->getOption($key);
378 return isset($resource['lib_options'][$key]) ? $resource['lib_options'][$key] : null;
382 * Normalize libmemcached options
384 * @param array|Traversable $libOptions
385 * @throws Exception\InvalidArgumentException
387 protected function normalizeLibOptions(& $libOptions)
389 if (! is_array($libOptions) && ! ($libOptions instanceof Traversable)) {
390 throw new Exception\InvalidArgumentException(
391 "Lib-Options must be an array or an instance of Traversable"
395 $result = [];
396 foreach ($libOptions as $key => $value) {
397 $this->normalizeLibOptionKey($key);
398 $result[$key] = $value;
401 $libOptions = $result;
405 * Convert option name into it's constant value
407 * @param string|int $key
408 * @throws Exception\InvalidArgumentException
410 protected function normalizeLibOptionKey(& $key)
412 // convert option name into it's constant value
413 if (is_string($key)) {
414 $const = 'Memcached::OPT_' . str_replace([' ', '-'], '_', strtoupper($key));
415 if (! defined($const)) {
416 throw new Exception\InvalidArgumentException("Unknown libmemcached option '{$key}' ({$const})");
418 $key = constant($const);
419 } else {
420 $key = (int) $key;
425 * Set servers
427 * $servers can be an array list or a comma separated list of servers.
428 * One server in the list can be descripted as follows:
429 * - URI: [tcp://]<host>[:<port>][?weight=<weight>]
430 * - Assoc: array('host' => <host>[, 'port' => <port>][, 'weight' => <weight>])
431 * - List: array(<host>[, <port>][, <weight>])
433 * @param string $id
434 * @param string|array $servers
435 * @return MemcachedResourceManager Provides a fluent interface
437 public function setServers($id, $servers)
439 if (! $this->hasResource($id)) {
440 return $this->setResource($id, [
441 'servers' => $servers
445 $this->normalizeServers($servers);
447 $resource = & $this->resources[$id];
448 if ($resource instanceof MemcachedResource) {
449 // don't add servers twice
450 $servers = array_udiff($servers, $resource->getServerList(), [$this, 'compareServers']);
451 if ($servers) {
452 $resource->addServers($servers);
454 } else {
455 $resource['servers'] = $servers;
458 return $this;
462 * Add servers
464 * @param string $id
465 * @param string|array $servers
466 * @return MemcachedResourceManager Provides a fluent interface
468 public function addServers($id, $servers)
470 if (! $this->hasResource($id)) {
471 return $this->setResource($id, [
472 'servers' => $servers
476 $this->normalizeServers($servers);
478 $resource = & $this->resources[$id];
479 if ($resource instanceof MemcachedResource) {
480 // don't add servers twice
481 $servers = array_udiff($servers, $resource->getServerList(), [$this, 'compareServers']);
482 if ($servers) {
483 $resource->addServers($servers);
485 } else {
486 // don't add servers twice
487 $resource['servers'] = array_merge(
488 $resource['servers'],
489 array_udiff($servers, $resource['servers'], [$this, 'compareServers'])
493 return $this;
497 * Add one server
499 * @param string $id
500 * @param string|array $server
501 * @return MemcachedResourceManager
503 public function addServer($id, $server)
505 return $this->addServers($id, [$server]);
509 * Normalize a list of servers into the following format:
510 * array(array('host' => <host>, 'port' => <port>, 'weight' => <weight>)[, ...])
512 * @param string|array $servers
514 protected function normalizeServers(& $servers)
516 if (! is_array($servers) && ! $servers instanceof Traversable) {
517 // Convert string into a list of servers
518 $servers = explode(',', $servers);
521 $result = [];
522 foreach ($servers as $server) {
523 $this->normalizeServer($server);
524 $result[$server['host'] . ':' . $server['port']] = $server;
527 $servers = array_values($result);
531 * Compare 2 normalized server arrays
532 * (Compares only the host and the port)
534 * @param array $serverA
535 * @param array $serverB
536 * @return int
538 protected function compareServers(array $serverA, array $serverB)
540 $keyA = $serverA['host'] . ':' . $serverA['port'];
541 $keyB = $serverB['host'] . ':' . $serverB['port'];
542 if ($keyA === $keyB) {
543 return 0;
545 return $keyA > $keyB ? 1 : -1;