前回(たくさんのパラメータを効率よく管理する)で触れたYAMLハンドラ。SyckよりはLibYAMLのほうがよさそうだな…とも思ったのだが、Windowsローカルに開発環境を作るのが非常に大変なので、相変わらずSpycを使うことにした。キャッシュはPEAR::Cache_Liteに変更して、読み込みだけキャッシングするラッパを実装してみた。

【KwappaYamlReader.php】

<?php
/**
*  YAMLリーダ with Cache_Lite
*
*  YAMLの読み込みとキャッシングを行う。
*/
//==============================================================================
// constants
//==============================================================================
define("KWAPPA_YAML_CACHE_DIR",       "/path/to/cache_dir/") ;
define("KWAPPA_YAML_CACHE_ID_PREFIX", "KWAPPA_YAML_CACHE") ;
//==============================================================================
// includes
//==============================================================================
require_once "Cache/Lite.php" ;
//==============================================================================
// class
//==============================================================================
/**
*  YAMLリーダ with Cache_Lite
*/
class KwappaYamlReader
{
    // Cache_Lite object
    private $cache ;
    // Cache_Lite用YAML向けパラメータ
    private static $CACHE_OPTIONS = array(
        'cacheDir'      => KWAPPA_YAML_CACHE_DIR,
        'lifeTime'      => null
    ) ;
    /**
     *  コンストラクタ
     */
    public function __construct()
    {
        $this->cache = new Cache_Lite(self::$CACHE_OPTIONS) ;
    }
    /**
     *  YAML読み込み
     *    @param        string    $fileName      YAMLファイル(フルパス)
     *    @returns  array                  パース済み配列
     *    @throws  RuntimeException   何らかのエラー
     */
    public function readYaml($fileName)
    {
        // キャッシュIDを生成する
        $cache_id = KWAPPA_YAML_CACHE_ID_PREFIX . "_" . md5($fileName) ;
        // キャッシュを取得
        $cached_yaml = $this->cache->get($cache_id) ;
        // 読み込もうとするファイルが存在しない
        if (!file_exists($fileName))
        {
            // ファイルが存在しないのにキャッシュが残ってるとイヤなので消去
            if ($cached_yaml)
            {
                $this->cache->remove($cache_id) ;
            }
            $this->raiseError("YAML [{$fileName}] is not found.") ;
        }
        // キャッシュヒット
        if ($cached_yaml)
        {
            // 更新されていればクリアして再キャッシュ
            if (filemtime($fileName) > $this->cache->lastModified())
            {
                $this->cache->remove($cache_id) ;
                $cached_yaml = null ;
            }
            // 更新されてなければこのデータを返す
            else
            {
                $cached_yaml = unserialize($cached_yaml) ;
            }
        }
        // キャッシュヒットしない : YAMLを読み込んでparse
        if (!$cached_yaml)
        {
            // YAMLを読み込んでパース
            require_once "lib/Spyc.php5" ;
            if (!$cached_yaml = Spyc::YAMLLoad($fileName))
            {
                $this->raiseError("Spyc::YAMLLoad failed.[{$fileName}]") ;
            }
            // キャッシング
            if (!$this->cache->save(serialize($cached_yaml), $cache_id))
            {
                $this->raiseError("failed to cache YAML [{$fileName}].") ;
            }
        }
        return $cached_yaml ;
    }
    /**
     *  何らかの不具合があったら例外を投げる
     *    @param        string    $msg      message
     */
    private function raiseError($msg)
    {
        throw new RuntimeException($msg) ;
    }
}
?>

Cache_Lite#setToDebugをコールすると、Cache_Lite内でエラーが発生した場合即死するようになる。実装初期のデバッグにはいいが、ある程度他に組み込みが進むと逆に不便そうなので、何か異常事態が発生したらRuntimeExceptionをthrowするようにしてある。

現在Cache_Liteのパッケージページを見に行くと大きく「このパッケージはメンテナンスされてないよー」と書いてある。一瞬ものすごく憂鬱な気分になるが、つい先月にStableリリースがあったばかり。どういうことなんだろう?とTrackbacksをたどってみたら、新任のメンテナが「ぼくの最初のリリースなの」とエントリしていたので一安心。ページのメンテナンスが追いついてないだけのようだ。

See also:
たくさんのパラメータを効率よく管理する