3 namespace Friendica\Core\Cache;
6 use Friendica\Core\Config\Configuration;
8 use Psr\Log\LoggerInterface;
13 * @author Hypolite Petovan <hypolite@mrpetovan.com>
15 class MemcachedCache extends Cache implements IMemoryCache
18 use TraitCompareDelete;
26 * @var LoggerInterface
31 * @var string First server address
37 * @var int First server port
42 * Due to limitations of the INI format, the expected configuration for Memcached servers is the following:
44 * 0 => "hostname, port(, weight)",
48 * @param array $memcached_hosts
52 public function __construct(string $hostname, Configuration $config, LoggerInterface $logger)
54 if (!class_exists('Memcached', false)) {
55 throw new Exception('Memcached class isn\'t available');
58 parent::__construct($hostname);
60 $this->logger = $logger;
62 $this->memcached = new Memcached();
64 $memcached_hosts = $config->get('system', 'memcached_hosts');
66 array_walk($memcached_hosts, function (&$value) {
67 if (is_string($value)) {
68 $value = array_map('trim', explode(',', $value));
72 $this->firstServer = $memcached_hosts[0][0] ?? 'localhost';
73 $this->firstPort = $memcached_hosts[0][1] ?? 11211;
75 $this->memcached->addServers($memcached_hosts);
77 if (count($this->memcached->getServerList()) == 0) {
78 throw new Exception('Expected Memcached servers aren\'t available, config:' . var_export($memcached_hosts, true));
85 public function getAllKeys($prefix = null)
87 $keys = $this->getOriginalKeys($this->getMemcachedKeys());
89 return $this->filterArrayKeysByPrefix($keys, $prefix);
93 * Get all memcached keys.
94 * Special function because getAllKeys() is broken since memcached 1.4.23.
96 * cleaned up version of code found on Stackoverflow.com by Maduka Jayalath
97 * @see https://stackoverflow.com/a/34724821
99 * @return array|int - all retrieved keys (or negative number on error)
101 private function getMemcachedKeys()
103 $mem = @fsockopen($this->firstServer, $this->firstPort);
104 if ($mem === false) {
108 // retrieve distinct slab
109 $r = @fwrite($mem, 'stats items' . chr(10));
115 while (($l = @fgets($mem, 1024)) !== false) {
123 // <STAT items:22:evicted_nonzero 0>
124 $r = preg_match('/^STAT\sitems\:(\d+)\:/', $l, $m);
130 if (!array_key_exists($a_slab, $slab)) {
136 foreach ($slab as $a_slab_key => &$a_slab) {
137 $r = @fwrite($mem, 'stats cachedump ' . $a_slab_key . ' 100' . chr(10));
142 while (($l = @fgets($mem, 1024)) !== false) {
150 // ITEM 42 [118 b; 1354717302 s]
151 $r = preg_match('/^ITEM\s([^\s]+)\s/', $l, $m);
161 // close the connection
167 foreach ($slab AS &$a_slab) {
169 foreach ($a_slab AS &$a_key) {
181 public function get($key)
184 $cachekey = $this->getCacheKey($key);
186 // We fetch with the hostname as key to avoid problems with other applications
187 $value = $this->memcached->get($cachekey);
189 if ($this->memcached->getResultCode() === Memcached::RES_SUCCESS) {
192 $this->logger->debug('Memcached \'get\' failed', ['result' => $this->memcached->getResultMessage()]);
201 public function set($key, $value, $ttl = Cache::FIVE_MINUTES)
203 $cachekey = $this->getCacheKey($key);
205 // We store with the hostname as key to avoid problems with other applications
207 return $this->memcached->set(
213 return $this->memcached->set(
223 public function delete($key)
225 $cachekey = $this->getCacheKey($key);
226 return $this->memcached->delete($cachekey);
232 public function clear($outdated = true)
237 return $this->memcached->flush();
244 public function add($key, $value, $ttl = Cache::FIVE_MINUTES)
246 $cachekey = $this->getCacheKey($key);
247 return $this->memcached->add($cachekey, $value, $ttl);
253 public function getName()
255 return self::TYPE_MEMCACHED;