]> git.mxchange.org Git - friendica.git/blob - src/Core/Cache/RedisCache.php
Merge pull request #7754 from annando/aria
[friendica.git] / src / Core / Cache / RedisCache.php
1 <?php
2
3 namespace Friendica\Core\Cache;
4
5 use Exception;
6 use Friendica\Core\Config\Configuration;
7 use Redis;
8
9 /**
10  * Redis Cache. This driver is based on Memcache driver
11  *
12  * @author Hypolite Petovan <hypolite@mrpetovan.com>
13  * @author Roland Haeder <roland@mxchange.org>
14  */
15 class RedisCache extends Cache implements IMemoryCache
16 {
17         /**
18          * @var Redis
19          */
20         private $redis;
21
22         /**
23          * @throws Exception
24          */
25         public function __construct(string $hostname, Configuration $config)
26         {
27                 if (!class_exists('Redis', false)) {
28                         throw new Exception('Redis class isn\'t available');
29                 }
30
31                 parent::__construct($hostname);
32
33                 $this->redis = new Redis();
34
35                 $redis_host = $config->get('system', 'redis_host');
36                 $redis_port = $config->get('system', 'redis_port');
37                 $redis_pw   = $config->get('system', 'redis_password');
38                 $redis_db   = $config->get('system', 'redis_db', 0);
39
40                 if (isset($redis_port) && !@$this->redis->connect($redis_host, $redis_port)) {
41                         throw new Exception('Expected Redis server at ' . $redis_host . ':' . $redis_port . ' isn\'t available');
42                 } elseif (!@$this->redis->connect($redis_host)) {
43                         throw new Exception('Expected Redis server at ' . $redis_host . ' isn\'t available');
44                 }
45
46                 if (isset($redis_pw) && !$this->redis->auth($redis_pw)) {
47                         throw new Exception('Cannot authenticate redis server at ' . $redis_host . ':' . $redis_port);
48                 }
49
50                 if ($redis_db !== 0 && !$this->redis->select($redis_db)) {
51                         throw new Exception('Cannot switch to redis db ' . $redis_db . ' at ' . $redis_host . ':' . $redis_port);
52                 }
53         }
54
55         /**
56          * (@inheritdoc)
57          */
58         public function getAllKeys($prefix = null)
59         {
60                 if (empty($prefix)) {
61                         $search = '*';
62                 } else {
63                         $search = $prefix . '*';
64                 }
65
66                 $list = $this->redis->keys($this->getCacheKey($search));
67
68                 return $this->getOriginalKeys($list);
69         }
70
71         /**
72          * (@inheritdoc)
73          */
74         public function get($key)
75         {
76                 $return = null;
77                 $cachekey = $this->getCacheKey($key);
78
79                 $cached = $this->redis->get($cachekey);
80                 if ($cached === false && !$this->redis->exists($cachekey)) {
81                         return null;
82                 }
83
84                 $value = unserialize($cached);
85
86                 // Only return a value if the serialized value is valid.
87                 // We also check if the db entry is a serialized
88                 // boolean 'false' value (which we want to return).
89                 if ($cached === serialize(false) || $value !== false) {
90                         $return = $value;
91                 }
92
93                 return $return;
94         }
95
96         /**
97          * (@inheritdoc)
98          */
99         public function set($key, $value, $ttl = Cache::FIVE_MINUTES)
100         {
101                 $cachekey = $this->getCacheKey($key);
102
103                 $cached = serialize($value);
104
105                 if ($ttl > 0) {
106                         return $this->redis->setex(
107                                 $cachekey,
108                                 $ttl,
109                                 $cached
110                         );
111                 } else {
112                         return $this->redis->set(
113                                 $cachekey,
114                                 $cached
115                         );
116                 }
117         }
118
119         /**
120          * (@inheritdoc)
121          */
122         public function delete($key)
123         {
124                 $cachekey = $this->getCacheKey($key);
125                 return ($this->redis->del($cachekey) > 0);
126         }
127
128         /**
129          * (@inheritdoc)
130          */
131         public function clear($outdated = true)
132         {
133                 if ($outdated) {
134                         return true;
135                 } else {
136                         return $this->redis->flushAll();
137                 }
138         }
139
140         /**
141          * (@inheritdoc)
142          */
143         public function add($key, $value, $ttl = Cache::FIVE_MINUTES)
144         {
145                 $cachekey = $this->getCacheKey($key);
146                 $cached = serialize($value);
147
148                 return $this->redis->setnx($cachekey, $cached);
149         }
150
151         /**
152          * (@inheritdoc)
153          */
154         public function compareSet($key, $oldValue, $newValue, $ttl = Cache::FIVE_MINUTES)
155         {
156                 $cachekey = $this->getCacheKey($key);
157
158                 $newCached = serialize($newValue);
159
160                 $this->redis->watch($cachekey);
161                 // If the old value isn't what we expected, somebody else changed the key meanwhile
162                 if ($this->get($key) === $oldValue) {
163                         if ($ttl > 0) {
164                                 $result = $this->redis->multi()
165                                         ->setex($cachekey, $ttl, $newCached)
166                                         ->exec();
167                         } else {
168                                 $result = $this->redis->multi()
169                                         ->set($cachekey, $newCached)
170                                         ->exec();
171                         }
172                         return $result !== false;
173                 }
174                 $this->redis->unwatch();
175                 return false;
176         }
177
178         /**
179          * (@inheritdoc)
180          */
181         public function compareDelete($key, $value)
182         {
183                 $cachekey = $this->getCacheKey($key);
184
185                 $this->redis->watch($cachekey);
186                 // If the old value isn't what we expected, somebody else changed the key meanwhile
187                 if ($this->get($key) === $value) {
188                         $result = $this->redis->multi()
189                                 ->del($cachekey)
190                                 ->exec();
191                         return $result !== false;
192                 }
193                 $this->redis->unwatch();
194                 return false;
195         }
196
197         /**
198          * {@inheritDoc}
199          */
200         public function getName()
201         {
202                 return self::TYPE_REDIS;
203         }
204 }