4 * A simple Memcached wrapper supporting namespacing of stored values.
6 * @author Andrew McMillan
7 * @license LGPL v2 or later
12 private static $servers;
13 private static $working;
16 * Initialise the cache connection. We use getpid() to give us a persistent connection.
18 function __construct() {
21 if ( isset(self
::$working) ) return;
23 self
::$working = false;
24 if ( isset($c->memcache_servers
) && class_exists('Memcached') ) {
25 dbg_error_log('Cache', 'Using Memcached interface connection');
26 self
::$servers = $c->memcache_servers
;
27 self
::$m = new Memcached();
28 foreach( self
::$servers AS $v ) {
29 dbg_error_log('Cache', 'Adding server '.$v);
30 $server = explode(',',$v);
31 if ( isset($server[2]) )
32 self
::$m->addServer($server[0],$server[1],$server[2]);
34 self
::$m->addServer($server[0],$server[1]);
36 self
::$working = true;
37 // Hack to allow the regression tests to flush the cache at start
38 if ( isset($_SERVER['HTTP_X_DAVICAL_FLUSH_CACHE'])) $this->flush();
41 dbg_error_log('Cache', 'Using NoCache dummy interface');
46 * So we can find out if we are actually using the cache.
49 return self
::$working;
53 * Construct a string from the namespace & key
54 * @param unknown_type $namespace
55 * @param unknown_type $key
57 private function nskey( $namespace, $key ) {
58 return str_replace(' ', '%20', $namespace . (isset($key) ?
'~~' . $key: '')); // for now.
62 * get a value from the specified namespace / key
66 function get( $namespace, $key ) {
67 if ( !self
::$working ) return false;
68 $ourkey = self
::nskey($namespace,$key);
69 $value = self
::$m->get($ourkey);
71 // if ( $value !== false ) dbg_error_log('Cache', 'Got value for cache key "'.$ourkey.'" - '.strlen(serialize($value)).' bytes');
76 * Set a value for the specified namespace/key, perhaps with an expiry (default 10 days)
82 function set( $namespace, $key, $value, $expiry=864000 ) {
83 if ( !self
::$working ) return false;
84 $ourkey = self
::nskey($namespace,$key);
85 $nskey = self
::nskey($namespace,null);
86 $keylist = self
::$m->get( $nskey, null, $cas_token );
87 if ( isset($keylist) && is_array($keylist) ) {
88 if ( !isset($keylist[$ourkey]) ) {
89 $keylist[$ourkey] = 1;
90 $success = self
::$m->cas( $cas_token, $nskey, $keylist );
92 while( !$success && $i++
< 10 && self
::$m->getResultCode() == Memcached
::RES_DATA_EXISTS
) {
93 $keylist = self
::$m->get( $nskey, null, $cas_token );
94 if ( $keylist === false ) return false;
95 if ( isset($keylist[$ourkey]) ) break;
96 $keylist[$ourkey] = 1;
97 $success = self
::$m->cas( $cas_token, $nskey, $keylist );
99 if ( !$success ) return false;
103 $keylist = array( $ourkey => 1 );
104 self
::$m->set( $nskey, $keylist );
107 // dbg_error_log('Cache', 'Setting value for cache key "'.$ourkey.'" - '.strlen(serialize($value)).' bytes');
108 return self
::$m->set( $ourkey, $value, $expiry );
112 * Delete a value from a namespace/key, or for everything in a namespace if a 'null' key is supplied.
116 function delete( $namespace, $key ) {
117 if ( !self
::$working ) return false;
118 $nskey = self
::nskey($namespace,$key);
119 dbg_error_log('Cache', 'Deleting from cache key "'.$nskey.'"');
121 self
::$m->delete( $nskey );
124 $keylist = self
::$m->get( $nskey, null, $cas_token );
125 if ( isset($keylist) ) {
126 self
::$m->delete( $nskey );
127 if ( is_array($keylist) ) {
128 foreach( $keylist AS $k => $v ) self
::$m->delete( $k );
135 * Flush the entire cache
138 if ( !self
::$working ) return false;
139 dbg_error_log('Cache', 'Flushing cache');
145 function getCacheInstance() {
146 static $ourCacheInstance;
148 if ( !isset($ourCacheInstance) ) $ourCacheInstance = new AWLCache('Memcached');
150 return $ourCacheInstance;