Codeigniterのキャッシュ、使えねえ…とお嘆きのあなた。分かりますよ。
DBカリカリやるのを出来るだけ避けたい、5インチフロッピー世代の俺なんかは、なるべくキャッシュ使いたいんですが、Codeigniterの キャッシュ機能って使いやすいのはいいんですが(一発キャッシュしておけば、自動的に読み込まれる超便利仕様)、エスケープもねえし、hookしてもあん まり意味ねえし、で困ってしまいますよね。
そこで、任意でパスを決めたら、その返り値をそのまんまキャッシュしてしまう部分キャッシュの仕組みを作ってみました。(まだ作ったばっかでろくすっぽ運用していないので、ご利用は自己責任でお願いします。とは言え、いくつかのサイトで実稼働済みっす)
準備
1. cacheフォルダのパーミッションを777にしておいてね。
2. CI_Outputを拡張して、MY_Outputを作ります。
3. 関数は二つ。set_cache()とget_cache()です。以下のコードをMY_Outputに貼付けてみるといいんじゃないかな。
function set_cache($path, $output, $expire = 60) {//部分キャッシュ
$CI =& get_instance();
$cache_path = ($CI->config->item('cache_path') == '') ? BASEPATH.'cache/' : $CI->config->item('cache_path');
if (!is_dir($cache_path) OR !is_really_writable($cache_path)) return FALSE;
//$pathからURIを作成〜ファイル名生成
$path = md5($path);
$dirname = substr($path, 0, 3);
$dirpath = $cache_path.'/'.$dirname.'/';
$filepath = $dirpath.$path;
if (!is_dir($dirpath)) mkdir($dirpath, 0777);
if (!$fp = @fopen($filepath, FOPEN_WRITE_CREATE_DESTRUCTIVE)) {
log_message('error', “Unable to write cache file: “.$filepath);
return;
}
$expire = time() + ($expire * 60);
if (flock($fp, LOCK_EX)) {
fwrite($fp, $expire.'TS—->'.$output);
flock($fp, LOCK_UN);
} else {
log_message('error', “Unable to secure a file lock for file at: “.$filepath);
return;
}
fclose($fp);
@chmod($filepath, DIR_WRITE_MODE);log_message('debug', “Cache file written: “.$filepath);
}
function get_cache($path) {//部分キャッシュを取得
$CI =& get_instance();
$cache_path = ($CI->config->item('cache_path') == '') ? BASEPATH.'cache/' : $CI->config->item('cache_path');
if (!is_dir($cache_path) OR !is_really_writable($cache_path)) return FALSE;
//$pathからURIを作成〜ファイル名生成
$path = md5($path);
$dirname = substr($path, 0, 3);
$dirpath = $cache_path.'/'.$dirname.'/';
$filepath = $dirpath.$path;
if (!is_dir($dirpath)) mkdir($dirpath, 0777);//フォルダが無ければ…作るだけだよな!
if (!@file_exists($filepath)) return FALSE;//ファイルが存在しない場合
if (!$fp = @fopen($filepath, FOPEN_READ)) return FALSE;//ファイルが開けない場合
flock($fp, LOCK_SH);
$cache = '';
if (filesize($filepath) > 0) $cache = fread($fp, filesize($filepath));//ファイル読込
flock($fp, LOCK_UN);
fclose($fp);
if (!preg_match(”/(\d+TS—->)/”, $cache, $match)) return FALSE;//タイムスタンプを確認
if (time() >= trim(str_replace('TS—->', '', $match['1']))) {
@unlink($filepath);
log_message('debug', “Cache file has expired. File deleted”);
return FALSE;
}
log_message('debug', “Cache file is current. Sending it to browser.”);
return str_replace($match['0'], '', $cache);
}
使い方
1. $CI->output->get_cache()を実行。第一引数には任意のパスを指定(REQUEST_URIそのまんまぶっ込んでもい いと思うし、完全任意でもOK。ただしユニークなの頼むよ)。返り値は、キャッシュファイルに書き込んで文字列がそのまんま返ってきます。
2. キャッシュされていなかったらfalseが返るので、その値をトリガーにして、何らかの処理を。
3. $CI->output->set_cache()を実行。第一引数にはget_cacheで使用した任意のパス、第二引数にはキャッシュを残したいデータを、そのままぶっ込みます。第三引数は、デフォルトのキャッシュと同様、保存しておく分数です。
4. これで次に同じ処理をしたときには、キャッシュが自動的に読み込まれます。
例:$json = $CI->output->get_cache($api_url);
if ($json === false) {
$json = file_get_contents($api_url);
$CI->output->set_cache($api_url, $json, 1);
}
このように、外部APIを叩いた結果なんかを保存しておくのに最適です!
ツッコミ等、余裕で歓迎するよ!