この例は、Kohana 3.1フレームワークでのコンテンツ管理システムの開発中に発生した1つの問題を解決した結果であり、1つの管理者アカウントと多くの未登録の読者が想定されています。
データベースにアクセスするモデルメソッドの結果をキャッシュするのに長い時間がかかりました。 実際、DBMSの負荷を軽減するために、データベースからデータセットのコピーを作成する必要がありました。 新しいデータを追加するとき、または古いデータを更新するときにキャッシュをすぐに更新するには、タグでキャッシュをクリアする必要がありました。
これらすべてを考慮し、使用するホスティングの制限により、要件は次のとおりでした。
- キャッシュはファイルに保存する必要があります。
- キャッシュを長期間保存して、データ抽出の速度を上げ、DBMSの負荷を軽減する必要があります。
- サイト管理者がデータを更新する場合、古いデータを含むキャッシュをクリアする必要があり、データベースからこのデータを直接抽出する関数の結果キャッシュだけでなく、結果がこのデータに関連付けられているキャッシュも削除する必要があります(たとえば、カタログセクションを削除する場合は、クリアする必要があります)カテゴリの見出しのリストのキャッシュ)。 これを実現するには、タグをサポートする必要があります。
- 目標を達成するために、特定のフレームワーク内で、新しいマテリアルを追加するためにかかる時間を犠牲にすることができます。これは、サードパーティのユーザーではなく「自分で」追加されるため、キャッシュのクリアなどに費やされます。
標準の
Cache_File
クラス
Cache_File
タグをサポート
Cache_File
ん。このため、独自のクラスを記述する必要があり、
JetCache
という名前が付けられました。
クラスは「loner」テンプレートに従って設計されています。 ファイルバンクのモデルで動作するクラスの例を考えてみましょう。 モデルが初期化されると、インスタンスが作成されます:
$this->cache = JetCache::instance();
特定のカテゴリのファイルのリストを抽出する関数の例を使用して、データキャッシュの作成を検討します(ここでは、読みやすくするためにいくつかの引数とコードを削除しています)。
したがって、キャッシュエントリはキー
$key = 'filebank_get_files'.$rubricId
とタグ“ filebank_rubrics”および“ filebank_files”、つまり、カテゴリとファイルに関する情報を直接更新する場合、このエントリをクリアする必要があります。
タグによってキャッシュをクリアする例については、カテゴリを削除する機能を検討してください。
cacheRegExp
プロパティには、検証のためにタグを抽出するファイル(キー)の名前の正規表現が含まれています。 つまり、チェックは二重です。最初に正規表現でファイル名をチェックし、次にタグをチェックします。
protected $cacheRegExp = '/^filebank/';
つまり、ルーブリックを削除すると、ルーブリックおよびルーブリック内の位置に関連付けられたデータのキャッシュがクリアされます。
したがって、プライマリの長期データキャッシュの可能性が追加されました。 コントローラレベルでは、ファイルドライバで標準の「キャッシュ」モジュールを使用して短期キャッシングを使用することもできます。
キャッシュを消去したり、ユーザーが情報を追加したりする高速が必要な大規模プロジェクトの場合は、もちろん、特別なソリューションを使用することをお勧めします。 たとえば、ドライバ「Memcached-tag」または「Xcache」モジュール「Cache」。 ただし、1人または少数のグループが管理する小規模なサイトの場合、特別なキャッシュツールを提供せずにホスティングを使用すると、このソリューションはうまく機能します。
キャッシュが保存されるファイルは1つのディレクトリに含まれ、次の構造を持ちます。
, (unix timestamp)\n
\n
\n
最後に、完全なクラスコードを示します。
<?php defined('SYSPATH') or die('No direct access allowed.'); class JetCache { protected static $instance = NULL; protected static $config; protected static $cache_dir; protected static $cache_time; public static function instance() { if (is_null(self::$instance)) { self::$instance = new self(); } return self::$instance; } protected function __construct() { self::$config = Kohana::config('jethelix')->default; self::$cache_dir = self::$config['jet_cache_dir']; if (!is_dir(self::$cache_dir)) { $oldUmask = umask(0000); if (!mkdir(self::$cache_dir, 0777, TRUE)) { $message = ' JetCache'; throw new Exception($message); } umask($oldUmask); } self::$cache_time = self::$config['jet_cache']; } protected function __clone() { } public function set($id, $data, array $tags=array(), $lifetime=NULL) { if (!$lifetime) { $lifetime = self::$cache_time; } $filename = self::$cache_dir . '/' . $id . '.txt'; $expires = time() + (int)$lifetime; $tagString = implode(',', $tags); $serData = serialize($data); $content = $expires . "\n" . $tagString . "\n" . $serData; try { file_put_contents($filename, $content); } catch (Exception $e) { return FALSE; } return TRUE; } public function get($id) { $filename = self::$cache_dir . '/' . $id . '.txt'; if (!is_file($filename)) { return NULL; } try { $content = file_get_contents($filename); } catch (Exception $e) { return NULL; } $arContent = explode("\n", $content); unset ($content); try { if ($arContent[0] < time()) { return NULL; } $data = unserialize($arContent[2]); return $data; } catch (Exception $e) { return NULL; } } public function delete($id) { $filename = self::$cache_dir . '/' . $id . '.txt'; try { unlink($filename); } catch (Exception $e) { return FALSE; } return TRUE; } public function garbage_collect() { $dir = opendir(self::$cache_dir); while ($file = readdir($dir)) { $fullName = self::$cache_dir . '/'. $file; if (!is_file($fullName)) { continue; } try { $this->_deleteIfExpires($fullName); } catch (Exception $e) { return FALSE; } } return TRUE; } protected function _deleteIfExpires($filename) { $fhandle = fopen($filename, 'r'); $expires = (int)fgets($fhandle); fclose($fhandle); if ($expires < time()) { unlink($filename); } } public function delete_by_tags(array $tags, $filenameRegExp=NULL) { $this->garbage_collect(); try { $arFiles = $this->_getTaggedFiles($tags, $filenameRegExp); $this->_deleteFiles($arFiles); } catch (Exception $e) { return FALSE; } return TRUE; } protected function _getTaggedFiles(array $needTags, $filenameRegExp) { $taggedFiles = array(); $dir = opendir(self::$cache_dir); while ($file = readdir($dir)) { $fullName = self::$cache_dir . '/' . $file; if (!is_file($fullName)) { continue; } if ($filenameRegExp && !preg_match($filenameRegExp, $file)) { continue; } $hasTags = $this->_getTagsFromFile($fullName); $isValid = $this->_tagsValidate($needTags, $hasTags); if ($isValid) { $taggedFiles[] = $fullName; } } return $taggedFiles; } protected function _getTagsFromFile($filename) { $fhandler = fopen($filename, 'r'); fgets($fhandler); $tagString = fgets($fhandler); fclose($fhandler); $tagString = trim($tagString); $arTags = explode(',', $tagString); return $arTags; } protected function _tagsValidate(array $needTags, array $hasTags) { foreach ($needTags as $tag) { if (in_array($tag, $hasTags)) { return TRUE; } } return FALSE; } protected function _deleteFiles(array $files) { foreach ($files as $filename) { unlink($filename); } } }