Memcached.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Andreas Fischer <bantu@owncloud.com>
  6. * @author Bart Visscher <bartv@thisnet.nl>
  7. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  8. * @author Joas Schilling <coding@schilljs.com>
  9. * @author Jörn Friedrich Dreyer <jfd@butonic.de>
  10. * @author Morris Jobke <hey@morrisjobke.de>
  11. * @author Robin Appelman <robin@icewind.nl>
  12. * @author Robin McCorkell <robin@mccorkell.me.uk>
  13. * @author Roeland Jago Douma <roeland@famdouma.nl>
  14. * @author Thomas Müller <thomas.mueller@tmit.eu>
  15. * @author Victor Dubiniuk <dubiniuk@owncloud.com>
  16. *
  17. * @license AGPL-3.0
  18. *
  19. * This code is free software: you can redistribute it and/or modify
  20. * it under the terms of the GNU Affero General Public License, version 3,
  21. * as published by the Free Software Foundation.
  22. *
  23. * This program is distributed in the hope that it will be useful,
  24. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  26. * GNU Affero General Public License for more details.
  27. *
  28. * You should have received a copy of the GNU Affero General Public License, version 3,
  29. * along with this program. If not, see <http://www.gnu.org/licenses/>
  30. *
  31. */
  32. namespace OC\Memcache;
  33. use OCP\HintException;
  34. use OCP\IMemcache;
  35. class Memcached extends Cache implements IMemcache {
  36. use CASTrait;
  37. /**
  38. * @var \Memcached $cache
  39. */
  40. private static $cache = null;
  41. use CADTrait;
  42. public function __construct($prefix = '') {
  43. parent::__construct($prefix);
  44. if (is_null(self::$cache)) {
  45. self::$cache = new \Memcached();
  46. $defaultOptions = [
  47. \Memcached::OPT_CONNECT_TIMEOUT => 50,
  48. \Memcached::OPT_RETRY_TIMEOUT => 50,
  49. \Memcached::OPT_SEND_TIMEOUT => 50,
  50. \Memcached::OPT_RECV_TIMEOUT => 50,
  51. \Memcached::OPT_POLL_TIMEOUT => 50,
  52. // Enable compression
  53. \Memcached::OPT_COMPRESSION => true,
  54. // Turn on consistent hashing
  55. \Memcached::OPT_LIBKETAMA_COMPATIBLE => true,
  56. // Enable Binary Protocol
  57. \Memcached::OPT_BINARY_PROTOCOL => true,
  58. ];
  59. /**
  60. * By default enable igbinary serializer if available
  61. *
  62. * Psalm checks depend on if igbinary is installed or not with memcached
  63. * @psalm-suppress RedundantCondition
  64. * @psalm-suppress TypeDoesNotContainType
  65. */
  66. if (\Memcached::HAVE_IGBINARY) {
  67. $defaultOptions[\Memcached::OPT_SERIALIZER] =
  68. \Memcached::SERIALIZER_IGBINARY;
  69. }
  70. $options = \OC::$server->getConfig()->getSystemValue('memcached_options', []);
  71. if (is_array($options)) {
  72. $options = $options + $defaultOptions;
  73. self::$cache->setOptions($options);
  74. } else {
  75. throw new HintException("Expected 'memcached_options' config to be an array, got $options");
  76. }
  77. $servers = \OC::$server->getSystemConfig()->getValue('memcached_servers');
  78. if (!$servers) {
  79. $server = \OC::$server->getSystemConfig()->getValue('memcached_server');
  80. if ($server) {
  81. $servers = [$server];
  82. } else {
  83. $servers = [['localhost', 11211]];
  84. }
  85. }
  86. self::$cache->addServers($servers);
  87. }
  88. }
  89. /**
  90. * entries in XCache gets namespaced to prevent collisions between owncloud instances and users
  91. */
  92. protected function getNameSpace() {
  93. return $this->prefix;
  94. }
  95. public function get($key) {
  96. $result = self::$cache->get($this->getNameSpace() . $key);
  97. if ($result === false and self::$cache->getResultCode() == \Memcached::RES_NOTFOUND) {
  98. return null;
  99. } else {
  100. return $result;
  101. }
  102. }
  103. public function set($key, $value, $ttl = 0) {
  104. if ($ttl > 0) {
  105. $result = self::$cache->set($this->getNameSpace() . $key, $value, $ttl);
  106. } else {
  107. $result = self::$cache->set($this->getNameSpace() . $key, $value);
  108. }
  109. return $result || $this->isSuccess();
  110. }
  111. public function hasKey($key) {
  112. self::$cache->get($this->getNameSpace() . $key);
  113. return self::$cache->getResultCode() === \Memcached::RES_SUCCESS;
  114. }
  115. public function remove($key) {
  116. $result = self::$cache->delete($this->getNameSpace() . $key);
  117. return $result || $this->isSuccess() || self::$cache->getResultCode() === \Memcached::RES_NOTFOUND;
  118. }
  119. public function clear($prefix = '') {
  120. // Newer Memcached doesn't like getAllKeys(), flush everything
  121. self::$cache->flush();
  122. return true;
  123. }
  124. /**
  125. * Set a value in the cache if it's not already stored
  126. *
  127. * @param string $key
  128. * @param mixed $value
  129. * @param int $ttl Time To Live in seconds. Defaults to 60*60*24
  130. * @return bool
  131. */
  132. public function add($key, $value, $ttl = 0) {
  133. $result = self::$cache->add($this->getPrefix() . $key, $value, $ttl);
  134. return $result || $this->isSuccess();
  135. }
  136. /**
  137. * Increase a stored number
  138. *
  139. * @param string $key
  140. * @param int $step
  141. * @return int | bool
  142. */
  143. public function inc($key, $step = 1) {
  144. $this->add($key, 0);
  145. $result = self::$cache->increment($this->getPrefix() . $key, $step);
  146. if (self::$cache->getResultCode() !== \Memcached::RES_SUCCESS) {
  147. return false;
  148. }
  149. return $result;
  150. }
  151. /**
  152. * Decrease a stored number
  153. *
  154. * @param string $key
  155. * @param int $step
  156. * @return int | bool
  157. */
  158. public function dec($key, $step = 1) {
  159. $result = self::$cache->decrement($this->getPrefix() . $key, $step);
  160. if (self::$cache->getResultCode() !== \Memcached::RES_SUCCESS) {
  161. return false;
  162. }
  163. return $result;
  164. }
  165. public static function isAvailable(): bool {
  166. return extension_loaded('memcached');
  167. }
  168. private function isSuccess(): bool {
  169. return self::$cache->getResultCode() === \Memcached::RES_SUCCESS;
  170. }
  171. }