2 /***************************************************************************
3 * Copyright (C) 2003-2010 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);
124 /** Global data storage. Global data is independent from
125 * the current session and can thus be shared by several
126 * PHP instances (for example using memcache if enabled).
128 * Global data can expire. The expire argument follow the
129 * semantic of the Memcache:: API:
130 * - 0 mean no timeout
131 * - <= 2592000 mean expires in $expire seconds
132 * - else $expire is an unix timestamp
135 public static function getGlobal($key, $callback = null
, $cbargs = null
,
138 return self
::get($key, self
::TIMER
, $callback, $cbargs, $expire);
141 public static function invalidateGlobal($key)
143 return self
::invalidate($key, self
::TIMER
);
146 public static function setGlobal($key, $var, $expire = 0)
148 return self
::set($key, self
::TIMER
, $var, $expire);
151 public static function hasGlobal($key)
153 return self
::has($key, self
::TIMER
);
157 /** Session data storage. Session data is session-dependent
158 * and thus must not be shared between sessions but can
159 * be stored in the $_SESSION php variable.
162 public static function getSession($key, $callback = null
, $cbargs = null
)
164 return self
::get($key, self
::SESSION
, $callback, $cbargs, 0);
167 public static function invalidateSession($key)
169 return self
::invalidate($key, self
::SESSION
);
172 public static function setSession($key, $var)
174 return self
::set($key, self
::SESSION
, $var, 0);
177 public static function hasSession($key)
179 return self
::has($key, self
::SESSION
);
183 /** Script local data storage. This stores data that
184 * expires at the end of the execution of the current
188 public static function getLocal($key, $callback = null
, $cbargs = null
)
190 return self
::get($key, self
::SCRIPT
, $callback, $cbargs, 0);
193 public static function invalidateLocal($key)
195 return self
::invalidate($key, self
::SCRIPT
);
198 public static function setLocal($key, $var)
200 return self
::set($key, self
::SCRIPT
, $var, 0);
203 public static function hasLocal($key)
205 return self
::has($key, self
::SCRIPT
);
210 /** Exception thrown when trying to get the value associated
211 * with a missing key.
213 class PlNotFoundInCacheException
extends PlException
215 public function __construct($key, $type)
217 parent
::__construct('Erreur lors de l\'accès aux données',
218 "Key '$key' not found in cache");
223 /** Interface for the storage backend.
225 interface PlCacheBackend
227 /** Return true if the backend contains the given key
228 * for the given storage type.
230 public function has($key, $type);
232 /** Set the value for the given key and type.
234 public function set($key, $type, $var, $expire);
236 /** Get the value for the given key and type.
238 * If the value is not found and a $callback is provided,
239 * call the function, pass $cbargs as arguments and use
240 * its output as the new value of the entry.
242 public function get($key, $type, $callback, $cbargs, $expire);
244 /** Remove the entry from the cache.
246 public function invalidate($key, $type);
249 class PlDummyCache
implements PlCacheBackend
251 public function has($key, $type)
256 public function set($key, $type, $var, $expire)
260 public function get($key, $type, $callback, $cbargs, $expire)
262 if (!is_null($callback)) {
263 return call_user_func_array($callback, $cbargs);
265 throw new PlNotFoundInCacheException($key, $type);
269 public function invalidate($key, $type)
274 abstract class PlArrayCache
implements PlCacheBackend
276 protected function getData(array $data, $key, $type)
278 $key = $this->arrayKey($key, $type);
279 if (!isset($data[$key])) {
280 throw new PlNotFoundInCacheException($key, $type);
282 if ($type == PlCache
::TIMER
) {
283 $entry = $data[$key];
284 $timeout = $entry['timeout'];
285 if (time() > $timeout) {
286 throw new PlNotFoundInCacheException($key, $type);
288 return $entry['data'];
293 protected function buildData($key, $type, $var, $expire)
295 if ($type == PlCache
::TIMER
) {
299 if ($expire <= 2592000) {
300 $expire = time() +
$expire;
302 return array('timeout' => $expire,
308 protected function getAndSetData(array $data, $key, $type,
309 $callback, $cbargs, $expire)
311 if (is_null($callback)) {
312 return $this->getData($data, $key, $type);
315 $value = $this->getData($data, $key, $type);
316 } catch (PlNotFoundInCacheException
$e) {
317 $value = call_user_func_array($callback, $cbargs);
318 $this->set($key, $type, $value, $expire);
324 protected abstract function arrayKey($key, $type);
326 public function has($key, $type)
329 $this->get($key, $type, null
, null
, 0);
331 } catch (PlNotFoundInCacheException
$e) {
337 class PlStaticCache
extends PlArrayCache
339 private $data = array();
341 protected function arrayKey($key, $type)
346 public function get($key, $type, $callback, $cbargs, $expire)
348 return $this->getAndSetData($this->data
, $key, $type,
349 $callback, $cbargs, $expire);
352 public function set($key, $type, $var, $expire)
354 $this->data
[$this->arrayKey($key, $type)]
355 = $this->buildData($key, $type, $var, $expire);
358 public function invalidate($key, $type)
360 unset($this->data
[$key]);
364 class PlSessionCache
extends PlArrayCache
366 public function __construct()
370 protected function arrayKey($key, $type)
372 return '__cache_' . $key;
375 public function get($key, $type, $callback, $cbargs, $expire)
377 return $this->getAndSetData($_SESSION, $key, $type,
378 $callback, $cbargs, $expire);
381 public function set($key, $type, $var, $expire)
383 S
::set($this->arrayKey($key, $type),
384 $this->buildData($key, $type, $var, $expire));
387 public function invalidate($key, $type)
389 S
::kill($this->arrayKey($key, $type));
393 class PlMemcacheCache
implements PlCacheBackend
397 public function __construct(array $servers)
399 $this->context
= new Memcache();
400 foreach ($servers as $address) {
401 /* XXX: Not IPv6 ready.
403 if (strpos($address, ':') !== false
) {
404 list($addr, $port) = explode(':', $address, 2);
405 $this->context
->addServer($addr, $port);
407 $this->context
->addServer($address);
412 public function has($key, $type)
414 return $this->context
->get($key) !== false
;
417 public function get($key, $type, $callbac, $cbargs, $expire)
419 $value = $this->context
->get($key);
420 if ($value === false
) {
421 if (is_null($callback)) {
422 throw new PlNotFoundInCacheException($key);
424 $value = call_user_func_array($callback, $cbargs);
425 $this->set($key, $type, $value, $expire);
430 public function set($key, $type, $var, $expire)
432 return $this->context
->set($key, $var, 0, $expire);
435 public function invalidate($key, $type)
437 return $this->context
->delete($key);
441 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8: