1 <?php
2
3 namespace Alo\Cache;
4
5 use Alo;
6 use Countable;
7 use ArrayAccess;
8 use IteratorAggregate;
9 use ArrayIterator;
10
11 if (!defined('GEN_START')) {
12 http_response_code(404);
13 } else {
14 /**
15 * The abstract cache class
16 *
17 * @author Art <a.molcanovas@gmail.com>
18 * @package Cache
19 */
20 abstract class AbstractCache implements Countable, ArrayAccess, IteratorAggregate {
21
22 /**
23 * Static reference to the last instance of the class
24 *
25 * @var AbstractCache
26 */
27 static $this;
28
29 /**
30 * Classes to check in "isAvailable()"
31 *
32 * @var array
33 */
34 private static $classes = ['Memcache', 'Memcached', 'Redis'];
35
36 /**
37 * The abstract client
38 *
39 * @var \Redis|\Memcache|\Memcached
40 */
41 protected $client;
42
43 /**
44 * Instantiates the class
45 *
46 * @author Art <a.molcanovas@gmail.com>
47 */
48 function __construct() {
49 if (!Alo::$cache) {
50 Alo::$cache = &$this;
51 }
52
53 self::$this = &$this;
54 }
55
56 /**
57 * Returns an iterator for a "foreach" loop
58 * @author Art <a.molcanovas@gmail.com>
59 * @return ArrayIterator
60 */
61 function getIterator() {
62 $all = $this->getAll();
63
64 return new ArrayIterator(is_array($all) ? $all : []);
65 }
66
67 /**
68 * Sets a value
69 * @author Art <a.molcanovas@gmail.com>
70 *
71 * @param string $key The key to set
72 * @param mixed $value The value to set
73 */
74 function offsetSet($key, $value) {
75 if ($key === null) {
76 $key = microtime(true);
77 }
78
79 $this->set($key, $value);
80 }
81
82 /**
83 * Checks if a key exists
84 * @author Art <a.molcanovas@gmail.com>
85 *
86 * @param string $key The key to look for
87 *
88 * @return bool
89 */
90 function offsetExists($key) {
91 return $this->get($key) !== null;
92 }
93
94 /**
95 * Deletes a key
96 * @author Art <a.molcanovas@gmail.com>
97 *
98 * @param string $key The key
99 */
100 function offsetUnset($key) {
101 $this->delete($key);
102 }
103
104 /**
105 * Returns the number of cached items
106 * @author Art <a.molcanovas@gmail.com>
107 * @return int
108 */
109 function count() {
110 $ga = $this->getAll();
111
112 return count($ga);
113 }
114
115 /**
116 * Checks if a caching extension is available
117 *
118 * @author Art <a.molcanovas@gmail.com>
119 * @return boolean
120 */
121 static function isAvailable() {
122 foreach (self::$classes as $class) {
123 if (class_exists('\\' . $class)) {
124 return true;
125 }
126 }
127
128 return false;
129 }
130
131 /**
132 * Calls a method of the caching client
133 *
134 * @author Art <a.molcanovas@gmail.com>
135 *
136 * @param string $method The method
137 * @param array $args Method args
138 *
139 * @return mixed Whatever the method returns
140 */
141 function __call($method, $args) {
142 return call_user_func_array([$this->client, $method], $args);
143 }
144
145 /**
146 * Key getter
147 *
148 * @author Art <a.molcanovas@gmail.com>
149 *
150 * @param string $key The key
151 *
152 * @return mixed
153 */
154 function __get($key) {
155 return $this->get($key);
156 }
157
158 /**
159 * Sets a value with its default expiration time
160 *
161 * @author Art <a.molcanovas@gmail.com>
162 *
163 * @param string $key The key
164 * @param mixed $val The value
165 *
166 * @return bool
167 */
168 function __set($key, $val) {
169 return $this->set($key, $val);
170 }
171
172 /**
173 * Gets a cached value
174 *
175 * @author Art <a.molcanovas@gmail.com>
176 *
177 * @param string $id The value's key
178 *
179 * @return mixed
180 */
181 abstract function get($id);
182
183 /**
184 * Sets a cached key/value pair
185 *
186 * @author Art <a.molcanovas@gmail.com>
187 *
188 * @param string $key The key identifier
189 * @param mixed $var The value to set
190 * @param int $expire When to expire the set data. Defaults to 3600s.
191 *
192 * @return boolean
193 */
194 abstract function set($key, $var, $expire = 3600);
195
196 /**
197 * Checks if a key is set in cache
198 *
199 * @author Art <a.molcanovas@gmail.com>
200 *
201 * @param string $key The key
202 *
203 * @return bool
204 */
205 function __isset($key) {
206 return $this->offsetExists($key);
207 }
208
209 /**
210 * Removes a key from cache
211 *
212 * @author Art <a.molcanovas@gmail.com>
213 *
214 * @param string $key The key
215 */
216 function __unset($key) {
217 $this->delete($key);
218 }
219
220 /**
221 * Gets a cached item
222 * @author Art <a.molcanovas@gmail.com>
223 *
224 * @param string $key The key
225 *
226 * @return mixed
227 */
228 function offsetGet($key) {
229 return $this->get($key);
230 }
231
232 /**
233 * Deletes a memcache key
234 *
235 * @author Art <a.molcanovas@gmail.com>
236 *
237 * @param string $key The key
238 *
239 * @return boolean
240 */
241 abstract function delete($key);
242
243 /**
244 * Clears all items from cache
245 *
246 * @author Art <a.molcanovas@gmail.com>
247 * @return boolean
248 */
249 abstract function purge();
250
251 /**
252 * Adds a server to the pool
253 *
254 * @author Art <a.molcanovas@gmail.com>
255 *
256 * @param string $ip The server IP
257 * @param int $port The server port
258 * @param int $weight The server's weight, ie how likely it is to be used
259 *
260 * @return boolean
261 */
262 abstract function addServer($ip, $port, $weight);
263
264 /**
265 * Deletes all cached entries with the supplied prefix
266 *
267 * @author Art <a.molcanovas@gmail.com>
268 *
269 * @param string $prefix The prefix
270 *
271 * @return AbstractCache
272 */
273 function deleteWithPrefix($prefix) {
274 $length = strlen($prefix);
275 $entries = array_keys($this->getAll());
276
277 \Log::debug('Deleting all cache entries with prefix ' . $prefix);
278 foreach ($entries as $key) {
279 if (substr($key, 0, $length) == $prefix) {
280 $this->delete($key);
281 }
282 }
283
284 return $this;
285 }
286
287 /**
288 * Return all cached keys and values
289 *
290 * @author Art <a.molcanovas@gmail.com>
291 * @return array
292 */
293 abstract function getAll();
294
295 /**
296 * Deletes all cached entries with the supplied suffix
297 *
298 * @author Art <a.molcanovas@gmail.com>
299 *
300 * @param string $suffix The suffix
301 *
302 * @return AbstractCache
303 */
304 function deleteWithSuffix($suffix) {
305 $length = strlen($suffix) * -1;
306 $entries = array_keys($this->getAll());
307
308 \Log::debug('Deleting all cache entries with suffix ' . $suffix);
309 foreach ($entries as $key) {
310 if (substr($key, $length) == $suffix) {
311 $this->delete($key);
312 }
313 }
314
315 return $this;
316 }
317
318 }
319 }
320