2 // vim: foldmethod=marker tabstop=4 shiftwidth=4 autoindent
7 * - キャッシュキーには250文字までしか使用できないので注意して下さい
9 * @todo ネームスペース/キャッシュキー長のエラーハンドリング
11 * @author Masaki Fujimoto <fujimoto@php.net>
17 * キャッシュマネージャクラス(memcache版)
19 * @author Masaki Fujimoto <fujimoto@php.net>
23 class Ethna_Plugin_Cachemanager_Memcache extends Ethna_Plugin_Cachemanager
25 /**#@+ @access private */
27 /** @var object MemCache MemCacheオブジェクト */
30 /** @var bool 圧縮フラグ */
33 /** @var array plugin configure */
34 var $config_default = array(
35 'host' => 'localhost',
51 $this->memcache_pool = array();
55 * memcacheキャッシュオブジェクトを生成、取得する
59 function _getMemcache($cache_key, $namespace = null)
61 $retry = $this->config['retry'];
62 $timeout = $this->config['timeout'];
66 list($host, $port) = $this->_getMemcacheInfo($cache_key, $namespace);
67 if (isset($this->memcache_pool["$host:$port"])) {
69 $this->memcache = $this->memcache_pool["$host:$port"];
70 return $this->memcache;
72 $this->memcache_pool["$host:$port"] =& new MemCache();
75 if ($this->config['use_pconnect']) {
76 $r = $this->memcache_pool["$host:$port"]->pconnect($host, $port, $timeout);
78 $r = $this->memcache_pool["$host:$port"]->connect($host, $port, $timeout);
87 trigger_error("memcache: connection failed");
88 $this->memcache_pool["$host:$port"] = null;
91 $this->memcache = $this->memcache_pool["$host:$port"];
92 return $this->memcache;
99 * @return array array(host, port)
100 * @todo $cache_keyから$indexを決める方法を変更できるようにする
102 function _getMemcacheInfo($cache_key, $namespace)
104 $namespace = $this->getNamespace($namespace);
106 $memcache_info = $this->config['info'];
107 $default_memcache_host = $this->config['host'];
108 $default_memcache_port = $this->config['port'];
110 if ($memcache_info == null || isset($memcache_info[$namespace]) == false) {
111 return array($default_memcache_host, $default_memcache_port);
114 // namespace/cache_keyで接続先を決定
115 $n = count($memcache_info[$namespace]);
117 $index = $cache_key % $n;
119 isset($memcache_info[$namespace][$index]['host']) ?
120 $memcache_info[$namespace][$index]['host'] :
121 $this->config['host'],
122 isset($memcache_info[$namespace][$index]['port']) ?
123 $memcache_info[$namespace][$index]['port'] :
124 $this->config['port'],
128 return array($default_memcache_host, $default_memcache_port);
134 * キャッシュに値が設定されている場合はキャッシュ値
135 * が戻り値となる。キャッシュに値が無い場合やlifetime
136 * を過ぎている場合、エラーが発生した場合はEthna_Error
140 * @param string $key キャッシュキー
141 * @param int $lifetime キャッシュ有効期間
142 * @param string $namespace キャッシュネームスペース
143 * @return array キャッシュ値
145 function get($key, $lifetime = null, $namespace = null)
147 $this->_getMemcache($key, $namespace);
148 if ($this->memcache == null) {
149 return Ethna::raiseError('memcache server not available', E_CACHE_NO_VALUE);
152 $namespace = $this->getNamespace($namespace);
154 $cache_key = $this->_getCacheKey($namespace, $key);
155 if ($cache_key == null) {
156 return Ethna::raiseError('invalid cache key (too long?)', E_CACHE_NO_VALUE);
159 $value = $this->memcache->get($cache_key);
160 if ($value == null) {
161 return Ethna::raiseError('no such cache', E_CACHE_NO_VALUE);
163 $time = $value['time'];
164 $data = $value['data'];
167 if (is_null($lifetime) == false) {
168 if (($time+$lifetime) < time()) {
169 return Ethna::raiseError('lifetime expired', E_CACHE_EXPIRED);
180 * @param string $key キャッシュキー
181 * @param string $namespace キャッシュネームスペース
182 * @return int 最終更新日時(unixtime)
184 function getLastModified($key, $namespace = null)
186 $this->_getMemcache($key, $namespace);
187 if ($this->memcache == null) {
188 return Ethna::raiseError('memcache server not available', E_CACHE_NO_VALUE);
191 $namespace = $this->getNamespace($namespace);
193 $cache_key = $this->_getCacheKey($namespace, $key);
194 if ($cache_key == null) {
195 return Ethna::raiseError('invalid cache key (too long?)', E_CACHE_NO_VALUE);
198 $value = $this->memcache->get($cache_key);
200 return $value['time'];
204 * 値がキャッシュされているかどうかを取得する
207 * @param string $key キャッシュキー
208 * @param int $lifetime キャッシュ有効期間
209 * @param string $namespace キャッシュネームスペース
211 function isCached($key, $lifetime = null, $namespace = null)
213 $r = $this->get($key, $lifetime, $namespace);
215 return Ethna::isError($r) ? false: true;
222 * @param string $key キャッシュキー
223 * @param mixed $value キャッシュ値
224 * @param int $timestamp キャッシュ最終更新時刻(unixtime)
225 * @param string $namespace キャッシュネームスペース
227 function set($key, $value, $timestamp = null, $namespace = null)
229 $this->_getMemcache($key, $namespace);
230 if ($this->memcache == null) {
231 return Ethna::raiseError('memcache server not available', E_CACHE_NO_VALUE);
234 $namespace = $this->getNamespace($namespace);
236 $cache_key = $this->_getCacheKey($namespace, $key);
237 if ($cache_key == null) {
238 return Ethna::raiseError('invalid cache key (too long?)', E_CACHE_NO_VALUE);
241 $time = $timestamp ? $timestamp : time();
242 $this->memcache->set($cache_key, array('time' => $time, 'data' => $value), $this->compress ? MEMCACHE_COMPRESSED : null);
249 * @param string $key キャッシュキー
250 * @param string $namespace キャッシュネームスペース
252 function clear($key, $namespace = null)
254 $this->_getMemcache($key, $namespace);
255 if ($this->memcache == null) {
256 return Ethna::raiseError('memcache server not available', E_CACHE_NO_VALUE);
259 $namespace = $this->getNamespace($namespace);
261 $cache_key = $this->_getCacheKey($namespace, $key);
262 if ($cache_key == null) {
263 return Ethna::raiseError('invalid cache key (too long?)', E_CACHE_NO_VALUE);
266 $this->memcache->delete($cache_key, -1);
273 * @param string $key キャッシュキー
274 * @param int $timeout ロックタイムアウト
275 * @param string $namespace キャッシュネームスペース
276 * @return bool true:成功 false:失敗
278 function lock($key, $timeout = 5, $namespace = null)
280 $this->_getMemcache($key, $namespace);
281 if ($this->memcache == null) {
282 return Ethna::raiseError('memcache server not available', E_CACHE_LOCK_ERROR);
286 $namespace = $this->getNamespace($namespace);
287 $cache_key = "lock::" . $this->_getCacheKey($namespace, $key);
291 $r = $this->memcache->add($cache_key, true, false, $lock_lifetime);
297 } while ($timeout > 0);
300 return Ethna::raiseError('lock timeout', E_CACHE_LOCK_TIMEOUT);
310 * @param string $key キャッシュキー
311 * @param string $namespace キャッシュネームスペース
312 * @return bool true:成功 false:失敗
314 function unlock($key, $namespace = null)
316 $this->_getMemcache($key, $namespace);
317 if ($this->memcache == null) {
318 return Ethna::raiseError('memcache server not available', E_CACHE_LOCK_ERROR);
321 $namespace = $this->getNamespace($namespace);
322 $cache_key = "lock::" . $this->_getCacheKey($namespace, $key);
324 $this->memcache->delete($cache_key, -1);
328 * ネームスペースからキャッシュキーを生成する
332 function _getCacheKey($namespace, $key)
335 $key = str_replace(":", "_", $key);
336 $cache_key = $namespace . "::" . $key;
337 if (strlen($cache_key) > 250) {
346 * MySQLなどいくつかの子クラスで有効
349 * @param bool $flag フラグ
351 function setCompress($flag) {
352 $this->compress = $flag;