Perl のテンプレートエンジンいろいろ

Web アプリケーション開発において、いまどきはソースコードに HTML をハードコーディングすることはなくなり、 テンプレートエンジンの利用が当たり前になりました。 Perl でもさまざまなフレームワークが登場しテンプレートエンジンをサポートしていますが、 独立したテンプレートエンジンも数多く存在します。 ここでは、私の独断と偏見でチョイスした Perl で良く使われる独立系のテンプレートエンジンをいくつか紹介します。

目次

テンプレートエンジンの特徴

Perl のテンプレートエンジンには、それぞれ特徴があります。 どのテンプレートエンジンが良いかは、みなさんの好みや状況によるため、一概に言えません。

テンプレートエンジンの選択の基準として注目したいのは、変数や制御の記述方法でしょう。 また、テンプレートエンジンによっては、サニタイジングや URL エンコードといった付加機能も用意されています。

テンプレートエンジンは必ずしも HTML 出力だけに使うとは限りません。 たとえば、XML の出力や、メール本文の出力もあり得ます。 このように用途によっても、自分にとってベストなテンプレートエンジンは異なるはずです。 ここでは、その判断ができるよう、それぞれのテンプレートエンジンの特徴を解説します。

以下では 4 つのテンプレートエンジンを試しますが、できる限り実践に近づけて、 utf8 プラグマを宣言 (use utf8) し、2 つの日本語を格納した変数をセットし、 HTML エスケープ (サニタイジング) したうえで、STDOUT に出力してみます。 いずれのサンプルコードも、最終的には次の文字列を出力します。

<p>コメント:ゴメン&lt;(_ _)&gt;(太郎さん)</p>

加えて、条件分岐、繰り返し処理についても、以下の HTML を出力するサンプルを比較します。

<ul>
    <li>太郎 (24)</li>
    <li>次郎 (21)</li>
</ul>

どのテンプレートエンジンも、テンプレートファイルのパスを指定して読み込むこともできますし、 結果をファイルとして出力することもできます。 しかし、ここでは、テンプレートはスカラー変数に格納したものを使い、結果もいったんスカラー変数に格納してから出力します。

非常にシンプルなサンプルコードですが、それぞれのテンプレートエンジンの特徴が良く分かると思います。

Template Toolkit

meta::cpan - Template Toolkit

Template Toolkit は Perl の世界では鉄板のテンプレートエンジンといっても過言ではないでしょう。 私が知る限り、Template Toolkit でやりたいことはほとんど実現できます。 それくらい機能が豊富です。 meta::cpan を見れば一通りのドキュメントを読むことができますが、 Template Toolkit の専用ウェブサイトもありますので、 そちらを参照するのも良いでしょう。

さらに私個人的に重宝するのは、レンタルサーバーにインストールされていなくても、ローカルにコピーして使えてしまう手軽さです。

Template Toolkit の特徴は、公式サイトを引用すると、高速で、柔軟性があり、拡張性が高いテンプレート処理システムです。 20 年以上も前になりますが、2003 年には O'Reilly Media, Inc. から書籍が出版されるほど有名なテンプレートエンジンですし、説明する内容が書籍になるほど機能が豊富にあるということです。

テンプレートの変数の書式は [% varname %] です。非常に短く分かりやすいのが特徴です。 しかし、このままでは HTML エスケープされないため、フィルターという機能を使って変換します。 HTML エスケープであれば [% varname | html %] と記述します。

use utf8;
use Encode;
use Template;

# テンプレート HTML
my $input = '<p>コメント:[% comment | html %]';
$input .= '([% nickname | html %]さん)</p>';

# 変数定義
my $vars = {
    comment  => 'ゴメン<(_ _)>',
    nickname => '太郎'
};

# オブジェクト生成
my $template = Template->new();

# 変数をセットして出力
my $output   = '';
$template->process( \$input, $vars, \$output );
print Encode::encode( 'UTF-8', $output );

Template Toolkit オブジェクトの process() メソッドは少しクセがあります。 テンプレートを処理した結果を返すのではなく、引数にセットしたスカラー変数 ($output) に結果がセットされます。

次に、条件分岐と繰り返し処理を見てみましょう。

use utf8;
use Encode;
use Template;

# テンプレート HTML
my $input = <<'EOT';
[% IF mlist.size -%]
<ul>
[% FOREACH member IN mlist -%]
    <li>[% member.name | html %] ([% member.age %])</li>
[% END -%]
</ul>
[% END -%]
EOT

# 変数定義
my $vars =
  { mlist => [ { name => '太郎', age => 24 }, { name => '次郎', age => 21 } ] };

# オブジェクト生成
my $template = Template->new();

# 変数をセットして出力
my $output = '';
$template->process( \$input, $vars, \$output );
print Encode::encode( 'UTF-8', $output );

[% IF mlist.size -%] の部分が条件分岐の構文です。 Perl っぽくないのですが、mlist.size で配列 mlist の要素数が得られます。

この構文の最後の -%] ですが、本来であればハイフンなしの %] で終端しても機能します。 しかし、ここにハイフンを加えると、末尾の改行が削除されてレンダリングされます。 Perl の組み込み関数の chomp のようなものです。 これによって、本来なら空白行がいくつもレンダリングされてしまうところが、それらがなくスッキリとしたレンダリング結果になります。

具体的に見せると、ハイフンなしならこのような結果になります。


<ul>

    <li>太郎 (24)</li>

    <li>次郎 (21)</li>

</ul>

ハイフンを入れることで、次のように空白行が詰められます。

<ul>
    <li>太郎 (24)</li>
    <li>次郎 (21)</li>
</ul>

このように、Template Toolkit は芸が細かいというか、かゆい所に手が届くのが大きな特徴ですね。

Template Toolkit の使い方の詳細については「Template Toolkit の使い方」をご覧ください。

HTML::Template

meta::cpan - HTML::Template

HTML::Template は、テンプレート変数や制御を HTML タグのように記述するのが特徴のテンプレートエンジンです。 前述の Template Toolkit と同様に、レンタルサーバーにインストールされていなくても、ローカルにコピーして使うことが可能です。

テンプレートの変数の書式は <TMPL_VAR NAME=varname> です。 しかし、このままでは HTML エスケープされません。 HTML エスケープする場合は <TMPL_VAR NAME=varname ESCAPE=HTML> と記述します。

use utf8;
use Encode;
use HTML::Template;

# テンプレート HTML
my $input = '<p>コメント:<TMPL_VAR NAME=comment ESCAPE=HTML>';
$input .= '(<TMPL_VAR NAME=nickname ESCAPE=HTML>さん)</p>';

# 変数定義
my $vars = {
    comment  => 'ゴメン<(_ _)>',
    nickname => '太郎'
};

# オブジェクト生成
my $template = HTML::Template->new( scalarref => \$input );

# 変数をセット
$template->param( comment  => $vars->{comment} );
$template->param( nickname => $vars->{nickname} );

# 出力
my $output = $template->output();
print Encode::encode( 'UTF-8', $output );

18 行目からの変数をセットする箇所に注目してください。 他のテンプレートエンジンと異なり、$vars をそのままセットすることはできません。 変数を一つずつセットしてあげる必要があります。この点が HTML::Template のデメリットの一つかもしれません。

次に、条件分岐と繰り返し処理を見てみましょう。

use utf8;
use Encode;
use HTML::Template;

# テンプレート HTML
my $input = <<'EOT';
<TMPL_IF NAME="msize">
<ul>
<TMPL_LOOP NAME="mlist">
    <li><TMPL_VAR NAME=name ESCAPE=HTML> (<TMPL_VAR NAME=age>)</li>
</TMPL_LOOP>
</ul>
</TMPL_IF>
EOT

# 変数定義
my $mlist = [ { name => '太郎', age => 24 }, { name => '次郎', age => 21 } ];
my $msize = scalar @{$mlist};

# オブジェクト生成
my $template = HTML::Template->new( scalarref => \$input );

# 変数をセット
$template->param( mlist => $mlist );
$template->param( msize => $msize );

# 出力
my $output = $template->output();
print Encode::encode( 'UTF-8', $output );

条件分岐も繰り返し処理も HTML タグっぽい記法です。 見慣れた記法のため、これが好きという人も多いかもしれません。

前述のサンプルコードの出力結果は次のようになります。 制御系タグ行による空白行を切り詰める機能はないため、空白行がいくつも登場してしまいます。


<ul>

    <li>太郎 (24)</li>

    <li>次郎 (21)</li>

</ul>

近年の開発環境のエディタにはコード整形機能があり、重宝しているのではないでしょうか。 適切なインデントやスペースなどを自動的に入れて、コードを見やすくする機能です。 ところが、HTML::Template の記法で書かれた HTML テンプレートは、 HTML の整形がまともに機能しないことが多いはずです。 とりわけ、インデントが狂うはずです。この点が HTML::Template の最も大きなデメリットでしょう。

実を言うと、私はこのコード整形に支障が出る問題のため、近年、 新規で HTML::Template を使うことは無くなりました。 しかし、エディタのコード整形機能をあまり使わないのであれば、 とても軽量でシンプルな HTML::Template は重宝するのではないでしょうか。

HTML::Template の使い方の詳細については「HTML::Template の使い方」 をご覧ください。

Template::Mustache

meta::cpan - Template::Mustache

Template::Mustache は、Mustache と呼ばれるテンプレート言語の Perl 実装です。 Mustache には、Perl だけでなく、Ruby, JavaScript, Python など、 さまざまなプログラミング言語でその実装が存在します。 各プログラミング言語の実装については、 Mustache の公式サイトをご覧ください。

Mustache という名前は、その名前の通り「髭」を想像するようなテンプレート記法から来ています。 実際には {{varname}} といった記法を採用しています。 この中カッコを 90 度傾けると、髭っぽく見えませんか?

Template::Mustache は前述の 2 つのテンプレートエンジンとは異なり、 テンプレートの変数 {{varname}} の置換において、 HTML エスケープ (サニタイジング) がデフォルトになります。 つまり、HTML エスケープするための追加の記法は不要です。 逆に、HTML サニタイジングしたくない場合は {{{varname}}} のように中カッコを 3 つ記述するか、{{& varname}} のように & を追記します。

個人的には Template::Mustache が最も好きなテンプレート記法なのですが、 残念ながらレンタルサーバーでローカルにコピーしただけでは使えません。 Template::Mustache がレンタルサーバーにインストールされていれば良いのですが、 恐らく、ほとんどのレンタルサーバーではインストールされていないと思われます。 ご自分で cpancpanm コマンドで Perl モジュールをインストールできる環境であれば、有力なテンプレートエンジンとなるでしょう。

use utf8;
use Encode;
use Template::Mustache;

# テンプレート HTML
my $input = '<p>コメント:{{comment}}';
$input .= '({{nickname}}さん)</p>';

# 変数定義
my $vars = {
    comment  => 'ゴメン<(_ _)>',
    nickname => '太郎'
};

# オブジェクト生成
my $mustache = Template::Mustache->new( template => $input );

# 変数をセットして出力
my $output = $mustache->render($vars);
print Encode::encode( 'UTF-8', $output );

Template::Mustache には条件分岐と繰り返し処理はありません。 というのも、テンプレート言語の Mustache 自体に条件分岐と繰り返し処理がないからです。 この点が Template::Mustache の最大のデメリットでしょう。

Text::Xslate

meta::cpan - Text::Xslate

テンプレートエンジンは数多く存在しますが、あらゆる機能をテンコ盛りにすると処理が遅くなり、 逆に処理を速くしようとすると機能が削られ不便になる、といったように、一長一短があります。 Text::Xslate は、その中間のほど良い具合のテンプレートエンジンとして開発されました。 しかし、パフォーマンスについては妥協がないようです。 テンプレートを中間コードにコンパイルしキャッシュする仕組みを導入することで、 他のテンプレートエンジンより高速に処理できると言われています。

Text::Xslate のテンプレートの変数の書式は <: $varname :> です。 テンプレートの変数の置換において、HTML エスケープ (サニタイジング) がデフォルトになります。 つまり、HTML エスケープするための追加の記法は不要です。 ここでは紹介しませんが、HTML エスケープしたくない場合、テンプレートの記述では制御できません。 コードの中で変数ごとに HTML エスケープしないよう指示する必要があります。

Text::Xslate は、残念ながらレンタルサーバーでローカルにコピーしただけでは使えません。 Text::Xslate がレンタルサーバーにインストールされていれば良いのですが、 恐らく、ほとんどのレンタルサーバーではインストールされていないと思われます。 ご自分で cpancpanm コマンドで Perl モジュールをインストールできる環境であれば、有力なテンプレートエンジンとなるでしょう。

use utf8;
use Encode;
use Text::Xslate;

# テンプレート HTML
my $input = '<p>コメント:<: $comment :>';
$input .= '(<: $nickname :>さん)</p>';

# 変数定義
my $vars = {
    comment  => 'ゴメン<(_ _)>',
    nickname => '太郎'
};

# オブジェクト生成
my $template = Text::Xslate->new();

# 変数をセットして出力
my $output   = $template->render_string($input, $vars);;
print Encode::encode( 'UTF-8', $output );

次に、条件分岐と繰り返し処理を見てみましょう。

use utf8;
use Encode;
use Text::Xslate;

# テンプレート HTML
my $input = <<'EOT';
: if $msize {
<ul>
: for $mlist -> $member {
    <li><: $member.name :> (<: $member.age :>)</li>
: }
</ul>
: }
EOT

# 変数定義
my $vars = {
    mlist => [ { name => '太郎', age => 24 }, { name => '次郎', age => 21 } ],
    msize => 2
};

# オブジェクト生成
my $template = Text::Xslate->new();

# 変数をセットして出力
my $output = $template->render_string( $input, $vars );
print Encode::encode( 'UTF-8', $output );

このコードの出力結果は次の通りです。

<ul>
    <li>太郎 (24)</li>
    <li>次郎 (21)</li>
</ul>

for や if といった制御構文がテンプレート構文というよりかはプログラミング言語の構文に近いのが特徴的ですね。 Perl の構文とも違うため最初のうちは書き間違うでしょうが、何度も書いていると書きやすいと感じられるでしょう。

: if $msize {」 や 「: for $mlist -> $member {」 のような制御構文の行は本来であれば空白行としてレンダリングされてもおかしくないのですが、 Text::Xslate はそのような空白行は自動的に削除してくれます。

まとめ

以上の内容から私が勝手に特徴を再整理すると、

といったところでしょうか。

自身で cpancpanm コマンドを使って Perl モジュールをインストールできる状況であれば、 以上 4 つの選択肢からお好みのものが選べます。一方、Perl モジュールをインストールできる状況でないなら、 Template Toolkit と HTML::Template の 2 つが選択肢になります。

以上、私の独断と偏見でテンプレートエンジンのチョイスと比較を行ってみましたが、いかがでしたでしょうか。 評価基準は状況によって大きく異なりますが、この記事の内容がテンプレートエンジン選定の参考になれば幸いです。