Perl Hackers Hub

第5回Xslate 次世代テンプレートエンジン(1)

本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回は藤吾郎さんで、テーマはXslateです。

はじめに

PerlとWebアプリケーションとの相性の良さは周知のとおりです。そして、Web開発にはテンプレートエンジンが欠かせません。テンプレートエンジンは、プレゼンテーションとロジックを分離し、デザイナとプログラマの分業を可能にし、MVCModel-View-ControllerのVViewを担う重要な要素です。

今回は、そんなテンプレートエンジンンの一つであり、筆者が開発しているXslateを紹介します。Xslateは2010年4月に開発を始めた新しいモジュールですが、速度・安定性・機能ともに高い水準になってきました。また、牧大輔氏や松野徳大氏をはじめとしたShibuya.pmの面々に多くのアドバイスをいただき、既存のテンプレートエンジンを置き換えられるくらい実用的になってきました。

なお、本記事のコードはPerl 5.8.1以上で動作します。また、Text::Xslateのバージョン0.2000に基づいています。

テンプレートエンジンとは

Xslateの解説に入る前に、テンプレートエンジン一般について概説します。テンプレートエンジンは基本的に、文字列に変数を埋め込むために使います。つまり、根本的な目的は組み込み関数のsprintf()と同じです。

たとえばリスト1のようなsprintf()を使ったコードがあるとします。単純な変数の埋め込みではこれで十分なのですが、sprintf()では埋め込む変数に名前を付けることができないため、この方法で大きなテキストやHTMLを生成するのは難しいでしょう。

リスト1 sprintf()でテキスト生成(use_sprintf.pl)
my $template = 'Hello, %s world!';
my $result   = sprintf $template, 'Template';
print $result, "\n"; # => 'Hello, Template world!'

そこでテンプレートエンジンを使うと、リスト2のように埋め込む変数に名前を付けることができます。コード量は増えてしまいましたが、これならば大きなテキストに大量の変数を埋め込むことが簡単にできます。

リスト2 Xslateでテキスト生成(use_xslate.pl)
use Text::Xslate;
my $tx       = Text::Xslate->new();
my $template = 'Hello, <: $lang :> world!';
my $result   = $tx->render_string(
    $template,
    { lang => 'Template' },
);
# => 'Hello, Template world!'

ほかにも一般的なテンプレートエンジンには、テンプレートをファイルから読み込んだり、配列などのコレクションを展開したりする機能がありますリスト3⁠。

リスト3 テンプレートの基本機能(basic_template.pl)
use Text::Xslate;
my $tx       = Text::Xslate->new();
my $template = <<'TMPL';
:# 配列をforループで展開する
: for $data -> $item {
    <: $~item.count :>. <: $item :>
: }
TMPL
my %vars     = (
    data => [qw(Apple Banana Pear)],
);
# ファイルから読み込んでレンダリング
print $tx->render_string($template, \%vars);
# =>
#    1. Apple
#    2. Banana
#    3. Pear

Webアプリケーションのような最終的にテキストを生成するアプリケーションでは、テンプレートエンジンが非常に役に立ちます。

Xslateに至る道

さて、PerlがWebアプリケーション開発に使われるようになったころから、無数のテンプレートエンジンが開発されてきました。古くからあるものとしてはHTML::TemplateやHTML::Masonなどがあり、またWebアプリケーションフレームワーク(以下WAF)が独自のテンプレートエンジンを実装していることも珍しくありません。

特に、以降で紹介する2つのテンプレートエンジン、Template Toolkit 2(以下TT2)Text::MicroTemplate(以下TMT)は、高機能で現在でもよく使われています。

Template Toolkit 2

TT2は非常に多機能で人気のあるテンプレートエンジンです。Catalystなど多数のWAFから利用できますし、TT2を解説する書籍も出版されています。TT2はPerlにおける事実上の標準テンプレートエンジンと言っても過言ではありません。TT2の基本的な使い方はリスト4のようになります。

リスト4 TT2の基本的な使い方(hello_tt2.pl)
use Template;
my $tt       = Template->new();
my $template = 'Hello, [% lang | html %] world!' . "\n";
$tt->process(\$template, { lang => '<TT2>' }, \*STDOUT)
    or die $tt->error;
# => 'Hello, &lt;TT2&gt; world!'

しかし、TT2は豊富な機能を持つ一方で、実行速度が遅いことがしばしば指摘されています。Template::TinyやTemplate::Alloyなど、TT2互換の軽量な実装がいくつか開発されていることは、TT2の人気と実行速度への不満の双方の特徴を表していると言えるでしょう。

また、TT2をHTML生成に使用する場合、安全なテンプレートを書くのが難しいというセキュリティ面での問題があります。リスト4では、htmlフィルタによってHTMLのメタ文字を手動でエスケープしているのにお気づきでしょうか。TT2ではテンプレート式のエスケープを常に意識しなければならないため、⁠エスケープ漏れ」による事故を防ぐのが難しいのです。

Text::MicroTemplate

速度と安全性というTT2の2つの問題を解決したテンプレートエンジンが、2008年にリリースされたTMTです。高速・安全で使いやすい実装として人気を博しており、Ark[1]Tatsumaki[2]などのWAFでは標準のテンプレートエンジンとして採用されています。

TMTはデフォルトでHTMLのメタ文字をエスケープします。リスト4をTMTで書き直したのがリスト5です。エスケープ処理を明示的に行う必要はないため、⁠エスケープ漏れ」の事故が起きる可能性は非常に少なくなっています。

リスト5 TMTの基本的な使い方(hello_tmt.pl)
use Text::MicroTemplate qw(:all);
my $template = 'Hello, <?= $_[0] ?> world!';
print render_mt($template, '<TMT>');
# => 'Hello, &lt;TMT&gt; world!'

また、TMTはPerlコードを埋め込むタイプのテンプレートエンジンであることも特徴です。テンプレート言語がPerlそのものであるため、Perlでできることならば何でもできます。したがってPerlエンジニアであれば使用するのは簡単であり、独自の構文を覚える必要もほとんどありません。

その一方で、Perl埋め込み型であることがTMTの欠点でもあります。Perlエンジニア以外にとっては使いやすいとは言えませんし、何でもできてしまうことがかえって危険なこともあるでしょう。

Text::Xslate

高性能なテンプレートエンジンがすでに多数あるという状況であれば、いまさら新たなテンプレートエンジンを開発しても「車輪の再発明」で終わることが多いでしょう。とはいえ、上述したように、それぞれのテンプレートエンジンには欠点もあります。

そこで、既存のテンプレートエンジンンの欠点を解決するべく開発したのがXslateです。TMTの基本的なコンセプトを継承しつつ、構文に制限を加えてさらに安全性を高めたテンプレートエンジンとして設計しました。

また、そもそも筆者がテンプレートエンジンに関心を持つようになったきっかけは速度でした。Cで書かれた、⁠高速」とうたわれる2つのテンプレートエンジン、HTML::Template::ProとClearSilverのソースコードを読んだところ、十分に最適化されているとは思えなかったのです。これらがCGIのような非永続環境を想定しているのも不満でした。速度が求められるWebアプリケーションであれば、永続環境で実行するのが今は普通でしょう。このような状況を踏まえたうえで設計と最適化を行えば、ずっと高速なテンプレートエンジンが書けるはずだと考えました。

TT2やTMTと同じ例をXslateで書くとリスト6のようになります。TMTと同様に、テンプレート変数は自動的にエスケープの対象となります。

リスト6 Xslateの基本的な使い方(hello_xslate.pl)
use Text::Xslate;
my $xslate   = Text::Xslate->new();
my %vars     = ( lang => '<Xslate>' );
my $template = 'Hello, <: $lang :> world!';
print $xslate->render_string($template, \%vars), "\n";
# => 'Hello, &lt;Xslate&gt; world!'

Xslateの特徴

Xslateはこのような背景のもとで設計されました。まず何よりも高速であり、しかも安全なテンプレートを簡単に書くことができます。

速度

まず速度ですが、筆者の知る限りでは、2010年8月現在、CPAN上で最速です。XslateはStarmanなどのPSGIサーバやmod_perl、FastCGIなどで動く永続的なWebアプリケーションで最高のパフォーマンスが出るように設計しており、この永続環境を想定したベンチマークではTT2の100倍以上、TMTよりも5~30倍程度の速度を出しています。

しかし、永続環境以外では必ずしも最速になるわけではありません。たとえばCGIのような非永続環境では、せいぜいTT2の5倍程度、TMTの2倍程度にとどまります。

この特徴を示すデータがSam Graham氏のレポート「Template Roundup」です。これはTemplate::BenchmarkというCPAN上の各種テンプレートエンジンを評価するモジュールをベースにしたレポートなのですが、永続環境として設定したinstance_reuseという条件では、Xslateが群を抜いて高速であることが示されています。しかし、それ以外の条件では特に目立った性能差はありません。

安全性

安全性については、⁠エスケープ漏れ」が基本的に起こらないようになっています。これはTMTから継承したコンセプトに従い、テンプレート式の結果を自動的にエスケープするためです。また、TMT同様に、文字列に対してそのまま出力されるよう指示する印を付けることができるため、二重エスケープも起こりません。

機能に制約を設けることでも安全性を高めています。テンプレート言語の自由度は強く制限されており、テンプレートから外部の環境にアクセスすることは基本的にはできません。また、デフォルトのテンプレート構文では、変数を変更する操作も許しません。

構文の選択

Xslateは複数のテンプレート構文をサポートしています。詳細は後述しますが、今のところコアではPerl 6風の独自言語Kolon、TT2互換のTTerseという構文が、拡張モジュールではPHPのSmarty互換のCleveryという構文があります。どちらの互換構文もデフォルトでHTMLエスケープを行うという点ではオリジナルと異なります。リスト7はTTerseとCleveryの例です。

リスト7 テンプレート構文を変更する(multi_syntaxes.pl)
use Text::Xslate;
use Text::Clevery;

my $tx = Text::Xslate->new(syntax => 'TTerse');
print $tx->render_string(<<'TX', { lang => '<TTerse>' });
Hello, [% lang %] world!
TX
# => Hello, &lt;TTerse&gt; world!

my $tc = Text::Clevery->new();
print $tc->render_string(<<'TC', { lang => '<Clevery>' });
Hello, { $lang } world!
TC
# => Hello, &lt;Clevery&gt; world!

なお、構文が異なっても基本的なテンプレート式は共通です。関数やメソッドの呼び出しはどの構文でもできるようになっていますし、豊富な演算子も使用できます。これらの構文やテンプレート式については次回以降に述べます。

おすすめ記事

記事・ニュース一覧