テンプレートエンジンを使おう

このレッスンで学ぶこと

ここでは、PHPのテンプレートエンジン「Smarty」について紹介します。テンプレートエンジンを使うと、デザインとロジックを分離することができます。それによりプログラムの見通しがよくなります。また、デザイナとプログラマが分業する場合にも威力を発揮します。

ここで学ぶポイント

  • テンプレートエンジンについて

ここで作るプログラムの例

ここでは、以前練習問題で扱った「小倉百人一首」のデータをテンプレートにあてはめて表示するという処理を作ってみます。

画面
画面

テンプレートエンジンを使った「百人一首」表示プログラム

テンプレートエンジンを使うと、ロジックとデザインを明確に分離することができます。このプログラムは2つのファイルから成り立っています。テンプレートエンジンを使ったプログラムを記述したPHPファイルと、画面のデザインを記述したテンプレートのHTMLファイルの2つです。(実際に、このプログラムを動かすには、テンプレートエンジンのSmartyを配置することが必要で、その方法はこの後で紹介します。)

まずは、テンプレートエンジンを使ったPHPのプログラムファイルから見てみましょう。

file: hyakunin.php

<?php
// Smartyの設定
require_once dirname(__FILE__).'/libs/smarty.class.php';
$smarty = new Smarty();
// ディレクトリの設定
$smarty->compile_dir  = dirname(__FILE__).'/template_c/';
$smarty->cache_dir    = dirname(__FILE__).'/cache/';
// 値を設定する
$smarty->assign("title", "小倉百人一首");
$smarty->assign("hyakunin", load_hyakunin_csv());
// テンプレートを表示する
$smarty->display('hyakunin.html');
// テンプレートに流し込むCSVデータを読み込む
function load_hyakunin_csv() {
    $csv = file_get_contents("hyakunin.csv");
    $csv = mb_convert_encoding($csv, "utf-8", "sjis");
    $res = array();
    foreach (explode("\n", $csv) as $row) {
        if (trim($row) == "" || intval($row[0]) == 0) continue;
        $c = explode(",", $row);
        $res[] = array("kami"=>$c[1], "simo"=>$c[2], "by"=>$c[5]);
    }
    return $res;
}

そして、以下が、テンプレート(デザイン)ファイルです。

file: hyakunin.html

<!DOCTYPE html>
<html lang="ja"><head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <title>{$title}</title>
    <style> body { line-height:1.9em; } </style>
</head><body>
<h1>{$title}</h1>
<ul>
{foreach from=$hyakunin key=i item=v}
    {if $i%2==0}
    <li style="background-color:#fffff0;">
    {else}
    <li style="background-color:#f0f0ff;">
    {/if}
    {$v.kami|escape} {$v.simo|escape} by {$v.by|escape}
    </li>
{/foreach}
</ul>
</body></html>

テンプレートエンジンについて

これまでも、PHPのプログラムをいくつか見てきました。PHPのプログラムが厄介なのは、PHPのプログラムには、PHPとHTMLが混在しているという点です。注意深く見ないと、どこがPHPのプログラムで、どこがHTMLのコードなのか分かりづらいと感じることでしょう。(筆者自身もPHPを使った多くの開発プロジェクトに参加してきましたが、PHPとHTMLがごちゃごちゃに書かれたプログラムを多く見てきました。)そして、分かりづらく読みづらいコードには、概して多くの書き間違いやバグが紛れ込みやすいものです。

「なんとか、PHPのプログラムを見やすくすることはできないだろうか。」そんな声に応えるようにしてテンプレートエンジンが登場しました。テンプレートエンジンを使えば、PHPのプログラムとHTML(とデザイン)を明確に分離することができるようになります。これにより、プログラマーとデザイナーの分業も可能になります。

とは言え、テンプレートエンジンというのは、それほど複雑なものではなく、基本的には、テンプレートとなるHTMLの中に、PHPの値を埋め込んで表示するという単純な仕組みとなっています。単純なテンプレートエンジンの処理を箇条書きにするなら、以下のようになります。

  • (1)テンプレートファイルを読み込む
  • (2)テンプレートで指示された部分に変数を埋め込む
  • (3)値を埋め込んだHTMLを表示する

テンプレートエンジン「Smarty」について

テンプレートエンジンの中でも、人気なのが「Smarty」です。Smartyを使うと、テンプレートに変数を埋め込むだけでなく繰り返しや条件分岐といった制御構文もテンプレートの中に記述することができます。また、プラグインの仕組みを持っており、埋め込む値をエスケープしたり、独自のフィルター処理をかける可能です。そして、テンプレートのコンパイルやキャッシュの仕組みにより、その動作が高速であることも人気の理由です。

Smartyは以下のWebサイトからダウンロードすることができます。サイト自体は英語ですが、親切に日本語のマニュアルも用意されています。

Smarty
http://www.smarty.net/
SmartyのWebサイト
SmartyのWebサイト

Smartyのダウンロードとプロジェクトへの配置

Smartyは、上記のWebサイトからダウンロードすることができます。(ここでは原稿執筆時点の最新安定版である3.0.8を利用しました。)ダウンロードしたならアーカイブを解凍し、その中にある「libs」フォルダをコピーしてプロジェクトに配置します。それから、Smartyでは動作を高速にするために一時ファイルを作成しますので、このためのフォルダ「template_c」「cache」も作成する必要があります。

ここでは、プロジェクトのフォルダ構成を以下のようにします。

+ <project>
| - hyakunin.php        ←メインPHPファイル
| - hyakunin.html       ←デザインテンプレート
| - hyakunin.csv        ←百人一首のCSVファイル
| + <libs>              ←Smartyの配置先
| + <template_c>        ←一時ファイルの保存用(コンパイルしたもの)
| + <cache>             ←一時ファイルの保存用(結果をキャッシュしたもの)

※百人一首のCSVファイルは以下よりダウンロードしてください。

http://kujirahand.com/web-tools/Hyakunin.php

Smartyの使い方

それでは、実際にSmartyを使ったプログラムを見ていきましょう。まず、Smartyを使うために、ライブラリの取り込みを行います。その後、Smartyのオブジェクトを作成し、一時ファイルを保存するために、フォルダの設定を行います。

<?php
// Smartyの設定
require_once dirname(__FILE__).'/libs/smarty.class.php';
$smarty = new Smarty();
// ディレクトリの設定
$smarty->compile_dir  = dirname(__FILE__).'/template_c/';
$smarty->cache_dir    = dirname(__FILE__).'/cache/';

そして、テンプレートで使う変数を、assign()メソッドを使って設定します。最後に、display()メソッドを使ってテンプレートを表示します。このとき引数にはどのテンプレートを使うのかファイル名を指定します。

// 値を設定する
$smarty->assign("title", "小倉百人一首");
$smarty->assign("hyakunin", load_hyakunin_csv());
// テンプレートを表示する
$smarty->display('hyakunin.html');

テンプレートの基本的な書き方

次に、テンプレートの書き方です。基本的に、テンプレートでは、assing()メソッドで指定した値を「{$変数名}」の書式で記述することになります。

以下のように書くと、「{$title}」と書いた部分が、「小倉百人一首」と置き換わります。

<h1>{$title}</h1>

また、これだけだと、PHPの文字列とそれほど変わりませんが、Smartyが便利なのは、プラグインで拡張可能なカスタム関数が手軽に適用できる点にあります。そのために「{$変数名|カスタム関数}」のように記述します。

例えば、以下のように書くと、タイトルにタグ記号を含んでいた時にも、escape関数が適用され、正しくエスケープを行ってくれます。

<h1>{$title|escape}</h1>

それから、Smartyを使うなら、配列の記述もシンプルになります。例えば、$fruits["apple"]["price"]という配列変数を記述したい場合には、テンプレートに以下のように記述するだけで済みます。

<div>{$fruits.apple.price}円</div>

テンプレートに繰り返しや条件分岐を記述する

さて、Smartyのforeachは、PHPと構文が若干異なりますが、記述する内容はほぼ同じです。{foreach}から{/foreach}までの間に繰り返し表示したいコンテンツを指定できます。特に便利なのは、{foreachelse}と書いて、配列の内容が空だったときの処理を記述できる点です。

[書式] Smartyでforeach
{foreach from=(配列) key=(キー変数名) item=(値変数名)}
    繰り返し表示するコンテンツをここに記述
{foreachelse}
    配列が空だったときのコンテンツをここに記述
{/foreach}

では、実際のコードを見てみましょう。百人一首のデータをforeach構文で順に表示する例です。{foreach}の指定の中で、item=vと指定することで、変数$vを使って、配列内のデータにアクセスすることができます。また、{$v.kami|escape}と書くことで、HTMLのエスケープ処理を行いつつ、$v["kami"]という配列内の要素にアクセスすることができています。

<ul>
{foreach from=$hyakunin item=v}
    <li>{$v.kami|escape} {$v.simo|escape} by {$v.by|escape}</li>
{foreachelse}
    <li>データがありません。</li>
{/foreach}
</ul>

また、{if}、{else}、{/if}を使って条件分岐を行うこともできます。

[書式]
{if (条件式)}
    条件式が真だったときのコンテンツ
{else}
    条件式が偽だったときのコンテンツ
{/if}

これを使って、奇数行と偶数行を交互に背景色を変更するのが以下のコードです。{foreach}の中で、key=iと書いているため、変数$iには、0、1、2...と配列の要素番号が代入されていきます。これを利用して、要素番号が2で割れるときは、背景色を薄紫色に、それ以外のときは薄い青色にしているのです。

<ul>
{foreach from=$hyakunin key=i item=v}
    {if $i%2==0}
    <li style="background-color:#fffff0;">
    {else}
    <li style="background-color:#f0f0ff;">
    {/if}
    {$v.kami|escape} {$v.simo|escape} by {$v.by|escape}
    </li>
{/foreach}

以上、テンプレートエンジン「Smarty」について紹介しました。テンプレートエンジンの便利さが分かったのではないでしょうか。より詳しい使い方は、Smartyのマニュアルをご覧ください。分かりやすく日本語でまとめられているので、それだけでも十分な情報となっています。