Fixes vim mode line.
[platal.git] / classes / plcache.php
CommitLineData
e3c13162
FB
1<?php
2/***************************************************************************
e92ecb8c 3 * Copyright (C) 2003-2011 Polytechnique.org *
e3c13162
FB
4 * http://opensource.polytechnique.org/ *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the Free Software *
18 * Foundation, Inc., *
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
20 ***************************************************************************/
21
22/** This class provide a common API for caching data.
23 */
24class PlCache
25{
26 /* Data types
27 */
28 const SCRIPT = 0x0001; /* The value expires after the execution of the script */
29 const SESSION = 0x0002; /* The value is session specific */
30 const TIMER = 0x0004; /* The value expires after some timeout */
31
32 private static $backends = array();
33
34 private static function getBackend($type)
35 {
36 if (isset(self::$backends[$type])) {
37 return self::$backends[$type];
38 }
39 $globals = Platal::globals();
40 if (($globals->debug & DEBUG_NOCACHE) != 0) {
41 $storage = 'none';
42 } else if (($globals->debug & DEBUG_SCRIPTCACHE) != 0
43 || php_sapi_name() == 'cli') {
44 $storage = 'static';
45 } else {
46 $storage = 'static';
47 switch ($type) {
48 case self::TIMER:
49 if ($globals->core->memcache) {
50 $storage = 'memcache';
51 break;
52 }
53
54 case self::SESSION:
55 $storage = 'session';
56 break;
57 }
58 }
59 if (!isset(self::$backends[$storage])) {
60 switch ($storage) {
61 case 'none':
62 self::$backends['none'] = new PlDummyCache();
63 break;
64
65 case 'static':
66 self::$backends['static'] = new PlStaticCache();
67 break;
68
69 case 'session':
70 self::$backends['session'] = new PlSessionCache();
71 break;
72
73 case 'memcache':
74 $servers = preg_split('/[, ]+/', $globals->core->memcache);
75 self::$backends['memcache'] = new PlMemcacheCache($servers);
76 break;
77 }
78 }
79 self::$backends[$type] = self::$backends[$storage];
80 return self::$backends[$type];
81 }
82
83
84 /** Get the value associated with the key in the cache.
85 *
86 * If the value does not exists, and a callback is provided,
87 * the value is built by calling the callback with the given
88 * expiration time.
89 *
90 * @throw PlNotFoundInCacheException if the value is not in the
91 * cache and $callback is null.
92 */
93 private static function get($key, $type, $callback, $cbargs, $expire)
94 {
95 $backend = self::getBackend($type);
96 return $backend->get($key, $type, $callback, $cbargs, $expire);
97 }
98
99 /** Invalidate the entry of the cache with the given name.
100 */
101 private static function invalidate($key, $type)
102 {
103 $backend = self::getBackend($type);
104 return $backend->invalidate($key, $type);
105 }
106
107 /** Set the value associated with the key in the cache.
108 */
109 private static function set($key, $type, $var, $expire)
110 {
111 $backend = self::getBackend($type);
112 return $backend->set($key, $type, $var, $expire);
113 }
114
115 /** Check if the key exists in the cache.
116 */
117 private static function has($key, $type)
118 {
119 $backend = self::getBackend($type);
120 return $backend->has($key, $type);
121 }
122
41c14698
FB
123 /** Clear the cache.
124 */
125 private static function clear($type)
126 {
127 $backend = self::getBackend($type);
128 $backend->clear($type);
129 }
130
131
132 /** Clear all the cached data.
133 */
134 public static function clearAll()
135 {
136 self::clearGlobal();
137 self::clearSession();
138 self::clearLocal();
139 }
e3c13162
FB
140
141 /** Global data storage. Global data is independent from
142 * the current session and can thus be shared by several
143 * PHP instances (for example using memcache if enabled).
144 *
145 * Global data can expire. The expire argument follow the
146 * semantic of the Memcache:: API:
147 * - 0 mean no timeout
148 * - <= 2592000 mean expires in $expire seconds
149 * - else $expire is an unix timestamp
150 */
151
152 public static function getGlobal($key, $callback = null, $cbargs = null,
153 $expire = 0)
154 {
155 return self::get($key, self::TIMER, $callback, $cbargs, $expire);
156 }
157
158 public static function invalidateGlobal($key)
159 {
160 return self::invalidate($key, self::TIMER);
161 }
162
163 public static function setGlobal($key, $var, $expire = 0)
164 {
165 return self::set($key, self::TIMER, $var, $expire);
166 }
167
168 public static function hasGlobal($key)
169 {
170 return self::has($key, self::TIMER);
171 }
172
41c14698
FB
173 public static function clearGlobal()
174 {
175 return self::clear(self::TIMER);
176 }
177
e3c13162
FB
178
179 /** Session data storage. Session data is session-dependent
180 * and thus must not be shared between sessions but can
181 * be stored in the $_SESSION php variable.
182 */
183
184 public static function getSession($key, $callback = null, $cbargs = null)
185 {
186 return self::get($key, self::SESSION, $callback, $cbargs, 0);
187 }
188
189 public static function invalidateSession($key)
190 {
191 return self::invalidate($key, self::SESSION);
192 }
193
194 public static function setSession($key, $var)
195 {
196 return self::set($key, self::SESSION, $var, 0);
197 }
198
199 public static function hasSession($key)
200 {
201 return self::has($key, self::SESSION);
202 }
203
41c14698
FB
204 public static function clearSession()
205 {
206 return self::clear(self::SESSION);
207 }
208
e3c13162
FB
209
210 /** Script local data storage. This stores data that
211 * expires at the end of the execution of the current
212 * script (or page).
213 */
214
215 public static function getLocal($key, $callback = null, $cbargs = null)
216 {
217 return self::get($key, self::SCRIPT, $callback, $cbargs, 0);
218 }
219
220 public static function invalidateLocal($key)
221 {
222 return self::invalidate($key, self::SCRIPT);
223 }
224
225 public static function setLocal($key, $var)
226 {
227 return self::set($key, self::SCRIPT, $var, 0);
228 }
229
230 public static function hasLocal($key)
231 {
232 return self::has($key, self::SCRIPT);
233 }
41c14698
FB
234
235 public static function clearLocal()
236 {
237 return self::clear(self::SCRIPT);
238 }
e3c13162
FB
239}
240
241
242/** Exception thrown when trying to get the value associated
243 * with a missing key.
244 */
245class PlNotFoundInCacheException extends PlException
246{
247 public function __construct($key, $type)
248 {
249 parent::__construct('Erreur lors de l\'accès aux données',
250 "Key '$key' not found in cache");
251 }
252}
253
254
255/** Interface for the storage backend.
256 */
257interface PlCacheBackend
258{
259 /** Return true if the backend contains the given key
260 * for the given storage type.
261 */
262 public function has($key, $type);
263
264 /** Set the value for the given key and type.
265 */
266 public function set($key, $type, $var, $expire);
267
268 /** Get the value for the given key and type.
269 *
270 * If the value is not found and a $callback is provided,
271 * call the function, pass $cbargs as arguments and use
272 * its output as the new value of the entry.
273 */
274 public function get($key, $type, $callback, $cbargs, $expire);
275
276 /** Remove the entry from the cache.
277 */
278 public function invalidate($key, $type);
41c14698
FB
279
280 /** Remove all the entries of the given type from the cache.
281 */
282 public function clear($type);
e3c13162
FB
283}
284
285class PlDummyCache implements PlCacheBackend
286{
287 public function has($key, $type)
288 {
289 return false;
290 }
291
292 public function set($key, $type, $var, $expire)
293 {
294 }
295
296 public function get($key, $type, $callback, $cbargs, $expire)
297 {
298 if (!is_null($callback)) {
299 return call_user_func_array($callback, $cbargs);
300 } else {
301 throw new PlNotFoundInCacheException($key, $type);
302 }
303 }
304
305 public function invalidate($key, $type)
306 {
307 }
41c14698
FB
308
309 public function clear($type)
310 {
311 }
e3c13162
FB
312}
313
314abstract class PlArrayCache implements PlCacheBackend
315{
316 protected function getData(array $data, $key, $type)
317 {
318 $key = $this->arrayKey($key, $type);
319 if (!isset($data[$key])) {
320 throw new PlNotFoundInCacheException($key, $type);
321 }
322 if ($type == PlCache::TIMER) {
323 $entry = $data[$key];
324 $timeout = $entry['timeout'];
325 if (time() > $timeout) {
326 throw new PlNotFoundInCacheException($key, $type);
327 }
328 return $entry['data'];
329 }
330 return $data[$key];
331 }
332
333 protected function buildData($key, $type, $var, $expire)
334 {
335 if ($type == PlCache::TIMER) {
336 if ($expire == 0) {
337 $expire = 2592000;
338 }
339 if ($expire <= 2592000) {
340 $expire = time() + $expire;
341 }
342 return array('timeout' => $expire,
343 'data' => $var);
344 }
345 return $var;
346 }
347
348 protected function getAndSetData(array $data, $key, $type,
349 $callback, $cbargs, $expire)
350 {
351 if (is_null($callback)) {
352 return $this->getData($data, $key, $type);
353 } else {
354 try {
355 $value = $this->getData($data, $key, $type);
356 } catch (PlNotFoundInCacheException $e) {
357 $value = call_user_func_array($callback, $cbargs);
358 $this->set($key, $type, $value, $expire);
359 }
360 return $value;
361 }
362 }
363
364 protected abstract function arrayKey($key, $type);
365
366 public function has($key, $type)
367 {
368 try {
369 $this->get($key, $type, null, null, 0);
370 return true;
371 } catch (PlNotFoundInCacheException $e) {
372 return false;
373 }
374 }
375}
376
377class PlStaticCache extends PlArrayCache
378{
379 private $data = array();
380
381 protected function arrayKey($key, $type)
382 {
383 return $key;
384 }
385
386 public function get($key, $type, $callback, $cbargs, $expire)
387 {
388 return $this->getAndSetData($this->data, $key, $type,
389 $callback, $cbargs, $expire);
390 }
391
392 public function set($key, $type, $var, $expire)
393 {
394 $this->data[$this->arrayKey($key, $type)]
395 = $this->buildData($key, $type, $var, $expire);
396 }
397
398 public function invalidate($key, $type)
399 {
400 unset($this->data[$key]);
401 }
41c14698
FB
402
403 public function clear($type)
404 {
405 $this->data = array();
406 }
e3c13162
FB
407}
408
409class PlSessionCache extends PlArrayCache
410{
411 public function __construct()
412 {
413 }
414
41c14698
FB
415 private function prefix($type)
416 {
417 return '__cache_' . $type . '_';
418 }
419
e3c13162
FB
420 protected function arrayKey($key, $type)
421 {
41c14698 422 return $this->prefix($type) . $key;
e3c13162
FB
423 }
424
425 public function get($key, $type, $callback, $cbargs, $expire)
426 {
427 return $this->getAndSetData($_SESSION, $key, $type,
428 $callback, $cbargs, $expire);
429 }
430
431 public function set($key, $type, $var, $expire)
432 {
433 S::set($this->arrayKey($key, $type),
434 $this->buildData($key, $type, $var, $expire));
435 }
436
437 public function invalidate($key, $type)
438 {
439 S::kill($this->arrayKey($key, $type));
440 }
41c14698
FB
441
442 public function clear($type)
443 {
444 $prefix = $this->prefix($type);
445 foreach ($_SESSION as $key=>$value) {
446 if (starts_with($key, $prefix)) {
447 unset($_SESSION[$key]);
448 }
449 }
450 }
e3c13162
FB
451}
452
453class PlMemcacheCache implements PlCacheBackend
454{
455 private $context;
456
457 public function __construct(array $servers)
458 {
459 $this->context = new Memcache();
460 foreach ($servers as $address) {
461 /* XXX: Not IPv6 ready.
462 */
463 if (strpos($address, ':') !== false) {
464 list($addr, $port) = explode(':', $address, 2);
465 $this->context->addServer($addr, $port);
466 } else {
467 $this->context->addServer($address);
468 }
469 }
470 }
471
472 public function has($key, $type)
473 {
474 return $this->context->get($key) !== false;
475 }
476
8f73b86b 477 public function get($key, $type, $callback, $cbargs, $expire)
e3c13162
FB
478 {
479 $value = $this->context->get($key);
480 if ($value === false) {
481 if (is_null($callback)) {
4ae22a90 482 throw new PlNotFoundInCacheException($key, $type);
e3c13162
FB
483 }
484 $value = call_user_func_array($callback, $cbargs);
485 $this->set($key, $type, $value, $expire);
486 }
487 return $value;
488 }
489
490 public function set($key, $type, $var, $expire)
491 {
492 return $this->context->set($key, $var, 0, $expire);
493 }
494
495 public function invalidate($key, $type)
496 {
497 return $this->context->delete($key);
498 }
41c14698
FB
499
500 public function clear($type)
501 {
502 return $this->context->flush();
503 }
e3c13162
FB
504}
505
fa7ffd66 506// vim:set et sw=4 sts=4 sws=4 foldmethod=marker fenc=utf-8:
e3c13162 507?>