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