2 /***************************************************************************
3 * Copyright (C) 2003-2011 Polytechnique.org *
4 * http://opensource.polytechnique.org/ *
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. *
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. *
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 *
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
20 ***************************************************************************/
22 /** This class provide a common API for caching data.
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 */
32 private static $backends = array();
34 private static function getBackend($type)
36 if (isset(self
::$backends[$type])) {
37 return self
::$backends[$type];
39 $globals = Platal
::globals();
40 if (($globals->debug
& DEBUG_NOCACHE
) != 0) {
42 } else if (($globals->debug
& DEBUG_SCRIPTCACHE
) != 0
43 ||
php_sapi_name() == 'cli') {
49 if ($globals->core
->memcache
) {
50 $storage = 'memcache';
59 if (!isset(self
::$backends[$storage])) {
62 self
::$backends['none'] = new PlDummyCache();
66 self
::$backends['static'] = new PlStaticCache();
70 self
::$backends['session'] = new PlSessionCache();
74 $servers = preg_split('/[, ]+/', $globals->core
->memcache
);
75 self
::$backends['memcache'] = new PlMemcacheCache($servers);
79 self
::$backends[$type] = self
::$backends[$storage];
80 return self
::$backends[$type];
84 /** Get the value associated with the key in the cache.
86 * If the value does not exists, and a callback is provided,
87 * the value is built by calling the callback with the given
90 * @throw PlNotFoundInCacheException if the value is not in the
91 * cache and $callback is null.
93 private static function get($key, $type, $callback, $cbargs, $expire)
95 $backend = self
::getBackend($type);
96 return $backend->get($key, $type, $callback, $cbargs, $expire);
99 /** Invalidate the entry of the cache with the given name.
101 private static function invalidate($key, $type)
103 $backend = self
::getBackend($type);
104 return $backend->invalidate($key, $type);
107 /** Set the value associated with the key in the cache.
109 private static function set($key, $type, $var, $expire)
111 $backend = self
::getBackend($type);
112 return $backend->set($key, $type, $var, $expire);
115 /** Check if the key exists in the cache.
117 private static function has($key, $type)
119 $backend = self
::getBackend($type);
120 return $backend->has($key, $type);
125 private static function clear($type)
127 $backend = self
::getBackend($type);
128 $backend->clear($type);
132 /** Clear all the cached data.
134 public static function clearAll()
137 self
::clearSession();
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).
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
152 public static function getGlobal($key, $callback = null
, $cbargs = null
,
155 return self
::get($key, self
::TIMER
, $callback, $cbargs, $expire);
158 public static function invalidateGlobal($key)
160 return self
::invalidate($key, self
::TIMER
);
163 public static function setGlobal($key, $var, $expire = 0)
165 return self
::set($key, self
::TIMER
, $var, $expire);
168 public static function hasGlobal($key)
170 return self
::has($key, self
::TIMER
);
173 public static function clearGlobal()
175 return self
::clear(self
::TIMER
);
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.
184 public static function getSession($key, $callback = null
, $cbargs = null
)
186 return self
::get($key, self
::SESSION
, $callback, $cbargs, 0);
189 public static function invalidateSession($key)
191 return self
::invalidate($key, self
::SESSION
);
194 public static function setSession($key, $var)
196 return self
::set($key, self
::SESSION
, $var, 0);
199 public static function hasSession($key)
201 return self
::has($key, self
::SESSION
);
204 public static function clearSession()
206 return self
::clear(self
::SESSION
);
210 /** Script local data storage. This stores data that
211 * expires at the end of the execution of the current
215 public static function getLocal($key, $callback = null
, $cbargs = null
)
217 return self
::get($key, self
::SCRIPT
, $callback, $cbargs, 0);
220 public static function invalidateLocal($key)
222 return self
::invalidate($key, self
::SCRIPT
);
225 public static function setLocal($key, $var)
227 return self
::set($key, self
::SCRIPT
, $var, 0);
230 public static function hasLocal($key)
232 return self
::has($key, self
::SCRIPT
);
235 public static function clearLocal()
237 return self
::clear(self
::SCRIPT
);
242 /** Exception thrown when trying to get the value associated
243 * with a missing key.
245 class PlNotFoundInCacheException
extends PlException
247 public function __construct($key, $type)
249 parent
::__construct('Erreur lors de l\'accès aux données',
250 "Key '$key' not found in cache");
255 /** Interface for the storage backend.
257 interface PlCacheBackend
259 /** Return true if the backend contains the given key
260 * for the given storage type.
262 public function has($key, $type);
264 /** Set the value for the given key and type.
266 public function set($key, $type, $var, $expire);
268 /** Get the value for the given key and type.
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.
274 public function get($key, $type, $callback, $cbargs, $expire);
276 /** Remove the entry from the cache.
278 public function invalidate($key, $type);
280 /** Remove all the entries of the given type from the cache.
282 public function clear($type);
285 class PlDummyCache
implements PlCacheBackend
287 public function has($key, $type)
292 public function set($key, $type, $var, $expire)
296 public function get($key, $type, $callback, $cbargs, $expire)
298 if (!is_null($callback)) {
299 return call_user_func_array($callback, $cbargs);
301 throw new PlNotFoundInCacheException($key, $type);
305 public function invalidate($key, $type)
309 public function clear($type)
314 abstract class PlArrayCache
implements PlCacheBackend
316 protected function getData(array $data, $key, $type)
318 $key = $this->arrayKey($key, $type);
319 if (!isset($data[$key])) {
320 throw new PlNotFoundInCacheException($key, $type);
322 if ($type == PlCache
::TIMER
) {
323 $entry = $data[$key];
324 $timeout = $entry['timeout'];
325 if (time() > $timeout) {
326 throw new PlNotFoundInCacheException($key, $type);
328 return $entry['data'];
333 protected function buildData($key, $type, $var, $expire)
335 if ($type == PlCache
::TIMER
) {
339 if ($expire <= 2592000) {
340 $expire = time() +
$expire;
342 return array('timeout' => $expire,
348 protected function getAndSetData(array $data, $key, $type,
349 $callback, $cbargs, $expire)
351 if (is_null($callback)) {
352 return $this->getData($data, $key, $type);
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);
364 protected abstract function arrayKey($key, $type);
366 public function has($key, $type)
369 $this->get($key, $type, null
, null
, 0);
371 } catch (PlNotFoundInCacheException
$e) {
377 class PlStaticCache
extends PlArrayCache
379 private $data = array();
381 protected function arrayKey($key, $type)
386 public function get($key, $type, $callback, $cbargs, $expire)
388 return $this->getAndSetData($this->data
, $key, $type,
389 $callback, $cbargs, $expire);
392 public function set($key, $type, $var, $expire)
394 $this->data
[$this->arrayKey($key, $type)]
395 = $this->buildData($key, $type, $var, $expire);
398 public function invalidate($key, $type)
400 unset($this->data
[$key]);
403 public function clear($type)
405 $this->data
= array();
409 class PlSessionCache
extends PlArrayCache
411 public function __construct()
415 private function prefix($type)
417 return '__cache_' . $type . '_';
420 protected function arrayKey($key, $type)
422 return $this->prefix($type) . $key;
425 public function get($key, $type, $callback, $cbargs, $expire)
427 return $this->getAndSetData($_SESSION, $key, $type,
428 $callback, $cbargs, $expire);
431 public function set($key, $type, $var, $expire)
433 S
::set($this->arrayKey($key, $type),
434 $this->buildData($key, $type, $var, $expire));
437 public function invalidate($key, $type)
439 S
::kill($this->arrayKey($key, $type));
442 public function clear($type)
444 $prefix = $this->prefix($type);
445 foreach ($_SESSION as $key=>$value) {
446 if (starts_with($key, $prefix)) {
447 unset($_SESSION[$key]);
453 class PlMemcacheCache
implements PlCacheBackend
457 public function __construct(array $servers)
459 $this->context
= new Memcache();
460 foreach ($servers as $address) {
461 /* XXX: Not IPv6 ready.
463 if (strpos($address, ':') !== false
) {
464 list($addr, $port) = explode(':', $address, 2);
465 $this->context
->addServer($addr, $port);
467 $this->context
->addServer($address);
472 public function has($key, $type)
474 return $this->context
->get($key) !== false
;
477 public function get($key, $type, $callback, $cbargs, $expire)
479 $value = $this->context
->get($key);
480 if ($value === false
) {
481 if (is_null($callback)) {
482 throw new PlNotFoundInCacheException($key);
484 $value = call_user_func_array($callback, $cbargs);
485 $this->set($key, $type, $value, $expire);
490 public function set($key, $type, $var, $expire)
492 return $this->context
->set($key, $var, 0, $expire);
495 public function invalidate($key, $type)
497 return $this->context
->delete($key);
500 public function clear($type)
502 return $this->context
->flush();
506 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8: