[Symfony2]入出力の文字エンコードを変換してみよう Symfony Advent Calender JP 2011 – 6日目-
@uechocoです。Symfony Advent Calendar JP 2011の6日目の記事です。@fivestrさんからのバトンタッチです。
日本Symfonyユーザー会に所属するスタッフとして、勢いで申し込んでみたものの、
普段職場でSymfonyを使ってないのでネタ探しに苦労しました。
そういえば去年のSymfony Advent 2010 : ATNDでは[Symfony2]PEAR::Net_UserAgent_MobileをDIコンテナから呼び出すという記事を書きました。
今年も似たような領域で、ガラケー向けのWebサイト開発で使えるかもしれないテクニックとして、入出力される値やコンテンツの文字コードを変換する方法をご紹介します。
symfony 1.x系ではMojavi由来のフィルタチェインの機構がありましたので、
MobileEncodingFilterなどのクラスを作って、入出力の前後でエンコードの変換をかませるというのが一般的だったかと思います。
Syfmony2系ではフィルタチェインという概念はなくなってしまいました。
代わりに処理の所々にイベントが定義されていて、リスナーを登録しておくと自動的にディスパッチしてくれるようなイベントドリブン方式に変わりました。
今回はSymfony2のHttpKernel機構に標準で設定されているkernel.requestイベントとkernel.responseイベントのリスナークラスとしてMobileEncodingListenerを作り、入出力のエンコード処理を行なっています。今回はSymfonyに標準で同梱されているAcme\DemoBundleの中に作成する前提です。
MobileEncodingListenerクラスの作成
まずはイベントリスナークラスを作成します。Symfony本体のFrameworkBundleやHttpKernelコンポーネントのディレクトリ構造の慣習に従ってAcme\DemoBundleの下にEventListenerディレクトリを作成し、その下にイベントリスナークラスを作成します。以下のようなMobileEncodingListnerクラスを作成してください。まだ中身は実装しません。
-
<?php
-
-
namespace Acme\DemoBundle\EventListener;
-
-
/**
-
* Convert input encoding or output encoding.
-
* @author uechoco <uechoco at gmail.com>
-
*
-
*/
-
class MobileEncodingListener
-
{
-
}
kernel.responseイベント
まずは簡単なkernel.responseイベントに対するメソッドを記述します。クラスの使用宣言を追加して、MobileEncodingListenerクラスにonKernelResponse()メソッドを実装します。
-
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
-
use Symfony\Component\HttpKernel\HttpKernelInterface;
-
/**
-
* Convert output encoding of response.
-
* @param FilterResponseEvent $event
-
*/
-
public function onKernelResponse(FilterResponseEvent $event)
-
{
-
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
-
return;
-
}
-
-
$response = $event->getResponse();
-
-
}
中身の処理としては単純で、Resopnseオブジェクトを取得し、その中のコンテンツの文字コードを変換してまたセットしなおしているだけです。今回は説明の簡略化のために文字コードの部分を直書きしています。最初の行のマスターリクエスト以外なら何もしないという条件文がありますが、HttpKernelではリクエストの種別が2種類あり、マスターリクエストとサブリクエストと呼ばれています。そのマスターリクエストの時だけ処理したいので条件式を記述しています。FilterResponseEventクラスを受け取るというのはkernel.responseイベントとの仕様として決められています。
kernel.requestイベント
次にkernel.requestイベントに対するメソッドを記述します。先ほどと同様にクラスの使用宣言を追加して、MobileEncodingListenerクラスにonKernelRequest()メソッドを実装します。
-
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
-
/**
-
* Convert input encoding of request.
-
* @param GetResponseEvent $event
-
*/
-
public function onKernelRequest(GetResponseEvent $event)
-
{
-
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
-
return;
-
}
-
-
$request = $event->getRequest();
-
-
// GET
-
$get = $request->query->all();
-
$request->query->replace($get);
-
// POST
-
$post = $request->request->all();
-
$request->request->replace($post);
-
}
こちらもマスターリクエストのみ処理する記述が最初にあります。次にRequestオブジェクトを取得し、今回は$_GETと$_POSTに相当する変数だけを文字コード変換しました。$_GETと$POSTはRequestオブジェクトの$queryと$requestというパブリック変数に代入されています。形式としてはParameterBagというクラスでラッピングされて代入されています。ParameterBagクラスのインターフェースとしてall()メソッドで全配列を取得し、replace()メソッドで全配列を総取替しています。
イベントリスナーとして登録する
最後に、先ほど作ったクラスとメソッドをイベントリスナーとして登録します。登録手順はDIコンテナにサービスとして登録するのと同じですが、特別なタグを定義することでイベントリスナーとして認識されるようになっています。src/Acme/DemoBundle/Resources/config/services.xmlを開き、以下のように追記してください。
-
<!-- 省略 -->
-
-
<parameters>
-
<parameter key="mobile_encoding_listener.class">Acme\DemoBundle\EventListener\MobileEncodingListener</parameter>
-
</parameters>
-
-
<services>
-
<!-- 省略 -->
-
<service id="acme.demo.mobile_encoding_listener" class="%mobile_encoding_listener.class%">
-
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" />
-
<tag name="kernel.event_listener" event="kernel.response" method="onKernelResponse" />
-
</service>
-
</services>
-
-
<!-- 省略 -->
確認する
何事もなければデモ画面が正常に動作すると思います。プロファイラのEvent項を見てみると、リスナーが登録されていることが確認できます。
今回直書きだった文字コードの指定は、config.ymlなどの設定ファイルから取ってくるような仕様にしたり、UserAgentを判別して決定するような仕様にしたほうが汎用性が高いでしょう。
余談
今回の記事の参考にするために他のイベントリスナークラスを見ていましたが、イベントリスナークラスの実装は様々なパターンが存在しているようです。例えば1つのリスナークラスに2つ以上のイベントメソッドをもたせているものや、1クラス1イベントメソッドのもの、他の役割のあるクラスにイベントメソッドを持たせてリスナークラスを兼任させているものなどです。
SymfonyのHttpKernelにどのようなイベントがあるか知りたい方は、Symfonyの内部構造のドキュメントの一節にイベントについて書かれている部分がありますので、そちらを参照してください。
最後に
7日目はMongoDB JPにも所属している@madapajaさんです。よろしくお願いします。


