cakephpで、viewのcacheではまったのでメモ

· application

cakephpで、レスポンス改善のためviewのhelperを利用したcacheを使おうと思ったのですが、cacheが生成されない現象にはまったので、そのときいろいろ調べたことをメモ。

よく見る利用方法は

  • コントローラにて、var $helpers = array(‘Cache’);を設定

  • app/config/core.phpにて、Configure::write(‘Cache.check’, true);を設定(コメントアウトをはずす)

  • キャッシュしたいアクションのコントローラで、cacheActionをarray(‘action_name’=>’保持期間(秒)’)という形で設定

の3点なのですが、これを実施しただけでは、私の環境では動作しませんでした。

気をつけないといけないポイントは、3つ。

  • cacheの書き出しは、View::render()。読み込みは、View::renderCache()

  • ハマリポイント!テンプレートの拡張子を変更したらキャッシュされない!

  • ハマリポイント!cacheActionは、配列でdurationキーが必要!

cacheの書き出し、読み込み

うまくキャッシュが生成されてなかったので、まず、ソースを追いかけて処理フローを調べてみました。(Cake.version = ‘1.2.3.8166′)

流れとしては、まず、書き込みは、

  • cake/cake/dispatcher.phpでview::render() (cake/cake/libs/view/view.php)を呼び出す

  • view::render()がview::_render()を呼び出す

  • view::_render()の4つ目の引数にtrueが渡っていれば、Cachehelper::cache()が呼びだされる

  • Cachehelper::cache()がCachehelper::->__writeFile()を呼んで、キャッシュ生成

読み込みは、

  • dispatcher->cached($url)で読み込もうとしているページのキャッシュの有無を判定

  • キャッシュがあれば、view::renderCache()を呼び出す

  • view::renderCache()がキャッシュを読み込む

となります。勘違いしそうなのが、Cacheクラスというのがありますが、それとは関係ないようで、core.phpでCache::configあたりを触っても、影響ありませんでした。(helperのcacheはview専用で、Cacheクラスの方はもっと汎用で高機能なcacheという感じでしょうか。)

ハマリポイント!テンプレートの拡張子を変更したらキャッシュされない!

デフォルトのviewテンプレートの拡張子は、「ctp」ですが、私はこれをapp_controller.phpにて「_html.php」に変えていました。

class AppController extends Controller {
// ビューファイルの拡張子
var $ext = "_html.php";
(・・・以下略)

そして、上のキャッシュの書き出し時のフローで呼び出される、view::_render()メソッドの呼び出し部分の処理が以下(cake/cake/view/view.phpの449行目~)

if (substr($layoutFileName, -3) === 'ctp' || substr($layoutFileName, -5) === 'thtml')
  $this->output = View::_render($layoutFileName, $data_for_layout, $loadHelpers, true);
} else {
  $this->output = $this->_render($layoutFileName, $data_for_layout, $loadHelpers);
}

$layoutFileNameには書き出すビューファイルのファイル名が入っており、その拡張子部分が「ctp」か「thtml」の場合に、_render()の4つ目の引数がtrueになります。つまり、phpという拡張子では、else側に行ってしまいキャッシュされません。(泣

(※ _render()の定義は、function _render($___viewFn, $___dataForView, $loadHelpers = true, $cached = false){} となっており、$cachedがデフォルトfalseなため)

これって拡張子変更に対応出来てないですよね?(汗

というわけで以下のようにview.php修正して、対応しました。(本当はコアはファイルは触りたくない・・・。何か他に良い方法があれば教えてください><)

// papettoTV modified
// if (substr($layoutFileName, -3) === 'ctp' || substr($layoutFileName, -5) === 'thtml') {
if (substr($layoutFileName, (-1)*strlen($this->ext)) === $this->ext) {
'thtml')
  $this->output = View::_render($layoutFileName, $data_for_layout, $loadHelpers, true);
} else {
  $this->output = $this->_render($layoutFileName, $data_for_layout, $loadHelpers);
}

設定している拡張子の文字数だけ抜き出して、比較しています。

ハマリポイント!cacheActionは、配列でdurationキーが必要!

これでCachehelper::cache()が呼び出されるようになりましたが、それでもキャッシュファイルが生成されませんでした。ソースを追いかけると(cake/cake/libs/view/helpers/cache.php の136行目~)

if ($cacheTime != '' && $cacheTime > 0) {
		$this->__parseFile($file, $out);
		if ($cache === true) {
			$cached = $this->__parseOutput($out);
			$this->__writeFile($cached, $cacheTime, $useCallbacks);
		}
		return $out;
	} else {
		return $out;
	}

$this->__writeFile()でキャッシュが生成されるようなのですが、$cacheTimeが0だったためif文を通っていませんでした。で、ソースを見る限り$cacheTimeに値が入るようにする方法はいくつかあるようですが、一番安全で確実だと判断して、Controller::cacheAction配列にdurationキーを設定する方法を選びました。もともとこう書いていたものを、

class HogeController extends AppController {
	var $cacheAction = array(
		"action_name" => 60
	);

以下のように変更しました。

class HogeController extends AppController {
	var $cacheAction = array(
		"duration"=>60
	);

アクション名が無くなっています。本当は、アクションごとにdurationを設定したいのですが、マニュアルに書いてる通りやってみても、やはりうまくいかず、とりいそぎ、コントローラ全体をキャッシュする設定で対応しました。(以下の追記にてさらに修正しました)

終わりに

アクションごとに設定できず、少し、気持ち悪い点が残ってますので、今後の課題にしたいと思います。うー、無念

また、今後の動作を確認していく中で、問題があれば報告したいと思います。

追記(2010/01/20)

var $cacheAction はルーティングを利用している場合は、アクション名ではなくURLで指定可能でした!

そこで

class HogeController extends AppController {
	var $cacheAction = array(
		"fuga" => array("duration" => 60)
	);

というようにすることで、/hoge/fuga で一度アクセスすると、60秒間有効なcacheファイルが生成されました。これで、各ページを自在にキャッシュコントロールできそうです。

画像未復旧: hatena.gif

関連があるかもしれないエントリー