symfonyのFabienさんと東京一日観光

symfonyユーザーの皆様

きたる5日(土)のPHPカンファレンス2009テックデイでは、symfonyのプロジェクトリーダー兼Sensio社の社長であるFabien Potencier(ファビエン・プートンシェ)さんがはるばるフランスから初来日し、symfonyプロジェクトについて講演されます。

今回が初来日、また講演の翌日6日(日曜日)がオフということで、Fabienさんと一緒に日本のsymfonyコミュニティ(ユーザー/開発者)のみなさんとめぐる東京一日観光を行ないます。

二日間のPHPカンファレンスの直後でお疲れかもしれませんが、symfonyの今後についてやプロジェクトの要望などについて直接Fabienさんと話せる貴重な機会になるかと思います(通訳は有志2,3名でお手伝いしますが、ちょっと勇気を出して直接英語で、あるいは仏語でお話してみるのもよいかと)。

ツアー名: (仮)symfonyについて語りつつ一日東京観光
日時: 2009年9月6日(日) 朝から夜まで
場所候補(検討中): 浅草寺・秋葉原・皇居・明治神宮/原宿・まだ大地に立ってればお台場ガンダム・都庁展望台・他に面白いところあれば
移動手段: 電車/徒歩メイン
参加資格: symfonyを使ってる、使いたい、これを機に○○○○○○○から切り替えてみようかな、という人。あと昼食夕食場所の手配とかもろもろ手伝ってくれる人
予定人数: 多くて10人ぐらい? そこまで居ないと思いますが、20人とか30人とかになったらたいへんかもと思いますがどうでしょう?

申し込み: 「a k i m o t o あっと gmail どっと com」へ、サブジェクトに「symfony東京観光参加希望」と入れてください。金曜夜締め切り

symfony1.2でsfSuperCachePluginを使う

symfonyでsuper cacheを実現するsfSuperCachePluginの、symfony1.2での使い方について。

super cacheは、動的にページを生成するWebアプリケーションにおいて、ほとんどの場合にWebサーバの仕組みを使って静的に作成したhtml(等)を直接クライアントに返すことでサーバの応答を早くし、サーバの負荷も軽減する手法です。

よく知られているのはWordPressのSuperCacheプラグインです。これを正しく設定すれば、動的生成でありながら静的生成のパフォーマンスを持つブログを運営することができます。

これまで自作でsuper cache相当の仕組みを作ったことはあるのですが、symfonyのプラグインがあるのでこれが使えるかどうか調べてみました。

とりあえず、READMEにあるように進めてみます。

プラグインのインストール

まず、プラグインはsymfony1.0にしか対応していませんので、普通にsymfonyコマンドでインストールしようとするとエラーになります。

コマンドインストールで失敗したときはいつもそうですが、パッケージを持ってきて自分で展開してみます。バージョンチェックが入ってるだけのことも多いので、これで動いてしまうプラグインも多いです。

> wget http://plugins.symfony-project.org/get/sfSuperCachePlugin/sfSuperCachePlugin-1.0.5.tgz

展開したら、sfSuperCachePlugin-1.0.5 というフォルダを、symfonyの作業フォルダ以下の plugins/sfSuperCachePlugin というフォルダにリネームしつつコピーします。

あとは、pluginsの下のプラグインを全部読むようになっていればsymfony ccするだけで自動的にロードされます。なってなければ、config/ProjectConfiguration.class.php のsetup()で、enableAllPluginsExcept()等を使って読み込まれるプラグインに指定してください。

このまま先へ進んでいくと、1.0と1.2の非互換でエラーになります。先に修正箇所を示すと、sfSuperCacheFilterの次の行


$uri = sfRouting::getInstance()->getCurrentInternalUri();

を、以下のように変更する必要があります。


$uri = sfContext::getInstance()->getRouting()->getCurrentInternalUri();

キャッシュ格納ディレクトリの用意

(symfonyアプリ)/web 以下に、静的ファイルの置き場を作ります。READMEにならって”cache”ディレクトリにします

> cd web
> mkdir cache

Un*xの場合はオーナーやパーミッションも調整してください

フィルタを噛ませる

プラグインの中に入ってるphpは、フィルタファイル一個だけです。これを(frontend)/filters.yml の # insert your own filters here のところに追加します。

supercache:
  class: sfSuperCacheFilter
  param:
    cache_dir: cache
    with_host: false

ホスト名を複数持たないならwith_hostはfalseでいいです。持つ場合、この後の設定も準じて変わるのでREADMEを読んでください。

リクエストがまず静的ファイルを見に行くように、.htaccessを修正

web/.htaccess を書き換えます。以下の2行のところを、


RewriteRule ^$ index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]

たとえば、以下のように書き換えます。


RewriteCond %{REQUEST_METHOD} GET
RewriteCond %{DOCUMENT_ROOT}/symfony_apps/sandbox/web/cache/supercache/%{PATH_INFO}.php -f
RewriteRule ^(.*)$ cache/$1.php [L]

この書き換え、READMEについてきたREQUEST_URIを使ったものが動かなかったので、自分で動くものを探してこんな風にしました。サブディレクトリにアプリを置いたりしなければREADMEのままのでも動くのかも。mod_rewriteは難しくてよくわからんです。

mod_rewriteが思うように動かないときは、とにかくhttpd.confの設定でrewrite logを取り、出たログを読みましょう。

やってるのは、


web/cache/ほげほげ/ふがふが.php

というファイルがアクセスされて、もしそれがあったら、そのファイルを直に実行して表示してしまう、という処理です。

もしファイルが無かったら、mod_rewriteの処理は下方のルールに落ちて行って、いつものフロントエンドコントローラ(web/index.php)を呼ぶようになっています。

これで、staticファイルが出来てればそのまま表示、出来てなければsymfonyを普通に実行(し、filters.ymlで挟んだフィルタがstaticファイルを生成)、というsuper cacheが完成となります。

super cacheを動かす条件

フィルタファイル sfSuperCacheFilter.class.php の中を読むとわかるのですが、super cacheが発動するには、いろいろな設定がされている必要があります。そうしないと、super cacheを動かしてたつもりが普通のキャッシュされたファイルを見てたりすることにもなります。

  • sf_cacheがtrue/onであること
  • $_GETや$_POSTのパラメータが無いこと
  • sf_debugがfalseであること、つまりデバッグモードでは呼ばれません
  • sf_no_script_nameがtrueであること
  • エラーコードが200(正常)であること。エラーページとかを403で返しているなら、それはsuper cacheの対象外です
  • そのmoduleのdefault cacheが enable: on であること
  • そのmodule/actionの cacheが enable: on であること
  • そのmodule/actionの with_layout: がtrueであること

キャッシュを使うことになってるページで、ページ全体をキャッシュして問題無く、GET/POSTパラメータも渡ってこない(パラメータが違えば普通ページ内容も変わりますから)という条件。

これ全部満たして、はじめてsuper cacheフィルタが効きます。

sfSuperCacheFilter::execute()のチェック文を、デバッガ等で確認しながらsettings.ymlやcache.ymlの設定を変え、(symfony ccもして、)動く設定になってることを確かめてください。

superキャッシュの動作確認

この状態でモジュールをつくり、適当にアクセスしてください。

frontend_dev.phpとか呼んじゃダメですよ。debug offなのでprodである/ (= index.php)を呼びます。


web/cache/(アプリ名)/(モジュール名)/(アクション名).php

などとファイルが出来ていたら、まずフィルタによるstaticファイル生成は合格です。

次に、もう一度アクセスしたときにこの生成されたファイルが開いてるのか、それともsymfony標準のcacheが開いてるのかを確認します。これは、cache/frontend/prod/template/… 以下の標準のキャッシュファイルを手で消してから、ブラウザでアクセスしてみるとわかります。staticなファイルが呼ばれて開かれていれば、標準のキャッシュは作られないはずです。

super cache完成か?

とまあ、プラグインで用意されているのはここまでです。しかし、生成されたstaticなファイルは拡張子が.phpなんですね。そこでそれらのファイルをエディタで開くと、先頭にphpのコードが一行入ってます。


<?php if (time() > 1241771234) { unlink(__FILE__); header('Location: '.$_SERVER['REQUEST_URI']); exit; } ?>

これで、自身のキャッシュ寿命を計りつつ、もし寿命が来ていたら自分自身を削除してもう一度同じURLにリダイレクト、とすることで、expireの処理を行なっているようです。

と、いうことは、このsuper cache、phpを回避してないのですね。厳密には super cacheと言えないのではと思います。たとえ一行とは言え、phpインタプリタをファイル毎に起動しているのです。

このプラグインはここまでなので、PHPを完全にスルーするsuper cacheの実現には、もう一手間かける必要があります。

生成するのは.phpじゃなく.htmlにし、もちろん先頭にphpコードは入れません。.htaccessの定義も.htmlに変えます。

そうなると、キャッシュのexpire判定は自分でやらせるわけには行きません。別のトリガーでこのキャッシュファイルを消すことになります。

たとえば、cronで動かしたスクリプトで定期的にこのcache/以下を見て、ファイル生成時を見つつ古すぎるものを消す、が一案。

もう一つは、CacheManagerで明示的にキャッシュをクリアされるタイミングで、このcache/以下の該当する静的ファイルも削除することです。

super cacheで用意したcache/以下のキャッシュファイルは、先頭行のPHPでexpireを自己診断した際しか消えないので、どのみちCacheManagerでのクリアをどうするかというのは検討しないといけないですね。

symfony1.2.6セキュリティリリース

syfony1.2系のアップデート1.2.6が出ました

セキュリティリリースということで、唯一件のバグ修正となっています。

1. symfonyのアドミンジェネレータを使っている人で、
2. unset()を明示的に呼ばずに
3. generator.ymlに書かないことでフィールドを隠すような使い方をしていた場合に、

攻撃を受ける可能性がある、というのをコアチーム内のレポートで発見し、修正したということです。

上記に当てはまるようなコードを書いた人で1.2.x系全部と次の1.3系ブランチを使っている人は、アップデートするか、パッチを当てるか、unset()を必ず呼ぶコードに直すか、するべきだということです。

# で、あってると思いますが、必ず原文にも当たってください

symfonyのキャッシュマネージャーが取れないとき

symfonyで、キャッシュが効いているページを強制的にクリアしたいとき、

  $cacheManager = $this->getContext()->getViewCacheManager();
  $cacheManager->remove('quiz/index?id='.$quizIdToRemove);

とかやってたのですが、デバッグ環境でエラーが出てしまいました。

Fatal error: Call to a member function set() on a non-object in ...

実環境でエラーにならないのでうんうん悩んで調べてみたら、デバッグ環境ではcache: off にしてました。getViewCacheManager()はキャッシュ使ってなければnullを返すようです。

# 考えてみたら当たりまえか

問題は、これがfatal errorで止まっていたためにトランザクションが完了していなかったことです。

cacheManagerを取得したときは、必ずnullチェックして、キャッシュが使われてるときだけ呼び出しましょう。 > 自分

symfony APIがブラウザから直接検索できるように(OpenSearch API)

本家ブログでの発表から。symfony APIの検索フォームが、OpenSearch APIで提供されるようになりました。

syfony公式ページに行くと、ブラウザ右上のサーチボックスが変化して、OpenSearchに対応していることを示してくれます。Firefox3ではこんな感じ。

symfony API 1.2を追加

すると、このあと、サーチボックスでsymfony APIが選べるので、ここにsymfonyフレームワークのクラス名や関数名を入れると、

APIマニュアルの該当ページにジャンプします。