Smarty勉強会
at 東京FORT

2005/08/20
By ishii(info@spanstyle.com)

この勉強会について目的
参加者の皆さんが帰り道で「もうsmartyを使った開発ができるかも？」と思えるようになること対象者
PHPをある程度使ったことがある、もしくはそれ同等のスキルを有する人で、Smartyテンプ
レートシステムに興味がある人
2テンプレートシステムって？
3

身近なテンプレートシステム

4テンプレート
システムを使うと、どんなメリットが？
5

テンプレートシステムを使わない開発
単一のPHPファイル内にロジック(PHP)とデザイン(HTML)が入ったソースになってしまうため、<?php$memo = $Container->GetData(DEF_FIELD_REPORT_MEMO);$pamphlet_pdf = $Container>GetData(DEF_FIELD_REPORT_PAMPHLET_PDF);// ID、更新ボタン$strId = "未登録";$pamphlet = NULL;
if($nStatus != DEF_RECORD_NEW){$strId = "<strong>".$id."</strong>";zz

プログラマとデザイナが同時作業しにくいロジックとデザインが混在していてわかりづらい$pamphlet = <<< EOF<tr><td class="attr"><strong>パンフレット</strong></td><td class="value">{$pamplhet_msg}<br /><input type="checkbox" name="nopamphlet" value="1"
id="nopamphlet"><label for="nopamphlet">パンフレットを削除する</label>
</td>
</tr>

EOF;
}
else {$pamphlet = <<< EOF?>

6

テンプレートシステムを使った開発
PHPファイルとテンプレートファイルに分かれ、PHPファ
イルにはロジック(PHP)を、テンプレートファイルにはデザイン(HTML)を書くようになるため、z プログラマとデザイナが同時作業しやすい
z ロジック用とデザイン用にファイルが分かれるので、見通しのいいソースになる<?php// * $_POST['__mode']の値で処理を分岐// */if (isset($_POST['__mode'])) {if ($_POST['__mode'] == 'query') {/** 絞込み中フラグを立てる*/$queryParam['query'] = true;} elseif ($_POST['__mode'] == 'clear') {}?><meta http-equiv="Content-Type" content="text/html; charset=euc-jp" /><title>{$title|default:"ようこそ"}</title><meta http-equiv="Pragma" content="no-cache" /><meta http-equiv="Cache-Control" content="no-cache" /><meta http-equiv="Expires" content="Thu, 01 Dec 1994 16:00:00 GMT"/>

<link rel="stylesheet" type="text/css" charset="euc-jp"
href="/manager.css" /><script type="text/javascript" src="manager.js"></script>

7

テンプレートシステムを利用するメリット
プログラマ側
デザインは気にすることなく、ひたすらロジックの組み立てに集中できてｳﾏｰ

デザイナ側
難解なロジックを目にすることなく、HTML/CSSの組み立てに集中できてｳﾏｰ

8

9

10

Smartyって何よ？
z 国内では最もメジャーなPHPのテンプレート

システム(テンプレートエンジン)※z 必要条件：PHP4.0.6以降（Smarty自体がPHPスクリプトでできているため、他には特に条件はない）※googleのヒット件数を見る限りでは、日本では一番使われてるっぽい11

Smartyのすごいところプログラマ側z キャッシュ機能でサーバ負荷軽減 ※z 独自プラグイン機能で好き勝手拡張し放題
z 独自フィルタ機能で好き勝手フィルタし放題
デザイナ側
z 修飾子で変数を自由に整形※
z 組み込み関数で表示制御※
z カスタム関数で工数削減※
今回は※の箇所をご紹介します。
12

Smartyの特徴
zzzzzzzzzzzzz

非常に高速
下仕事はPHPパーサが行うので能率的コンパイルは一度だけ行われるので、テンプレートのパースによるオーバーヘッドが無い
変更されたテンプレートファイルのみ再コンパイルを行うのでスマートカスタム関数及び 変数の修正子をカスタム定義する事によって、 テンプレート言語を強力に拡張する事が可能
テンプレート言語の開始と終端を表すデリミタタグの記法を変更可能 (例: { }, {{ }}, <!--{ }-->, 等)
if/elseif/else/endif ステートメントはPHPパーサに渡されて処理されるので、{if ...}の条件式にはシンプルな式から複雑な式まで自由に指定可能
section, if等は無制限にネスト可能
テンプレートエンジンはカスタマイズできるので必要ない(又は推奨されない)かもしれないが、 テンプレートファイルにPHPコードを埋め込む事が可能
キャッシュ機能をサポート
テンプレートリソースのサポート
カスタムキャッシュハンドラ関数
プラグイン構造
出展元：Smarty日本語 マニュアル13

コンパイル？
14

コンパイルって何よ？
テンプレートファイルにおけるSmartyの独自記述部分をパース(解析)し、実行可能なPHPスクリプトへ変換する作業のこと。
コンパイルは通常Smartyによって自動的に行われるので、利用者側では特に意識する必要はない。

テンプレートファイル(index.tpl)：<body><h1>{$title|default:"タイトルを設定してください"}</h1><p>{$contents|default:"コンテンツを設定してください"}</p></body>コンパイル済みのテンプレートファイル(%%45^45E^45E480CD%%index.tpl.php)：<body><h1><?php echo ((is_array($_tmp=@$this->_tpl_vars['title'])) ? $this>_run_mod_handler('default', true, $_tmp, "タイトルを設定してください") :smarty_modifier_default($_tmp, "タイトルを設定してください")); ?></h1><p><?php echo ((is_array($_tmp=@$this->_tpl_vars['contents'])) ? $this>_run_mod_handler('default', true, $_tmp, "コンテンツを設定してください") : smarty_modifier_default($_tmp, "コンテンツを設定してください")); ?></p></body>15

コンパイルの内部処理はこんな感じ（あくまでもイメージです）
テンプレートを指定して表示要求

そのテンプレートはコンパイル済みか？

no（コンパイル前の）テンプレートを読み込みコンパイル処理

yes
コンパイル済み
テンプレートを取得

コンパイル済み
テンプレートを生成

出力

参考元：極める！PHP (p73) / 翔泳社発行
16

前説
ここまで17

まずは
下準備18日本語マニュアルとSmarty本体の入手
日本語マニュアルの入手
http://sunset.freespace.jp/smarty/
有志の方(Shinsuke Matsuda氏)のサイト。掲示板によればhttp://nidieu.flnet.org/smartylabo/が移転先となっているが、長い間アクセス不可能になっている。移転先での最新版マニュアルは2.6.7だったが、旧サイトでは今のところ2.6.6のマニュアルしか入手できない模様。HTML版とchm版(Windowsヘルプ形式)で配布されているけど、個人的には見やすくて検索ができるchm版がお勧め

Smarty本体の入手
http://smarty.php.net/
オフィシャルサイト。2005/08/18時点での最新版は2.6.10となっている。オフィシャルサイトには日本語版マニュアルはないけど、英語版とかなら最新のものがあるのでチェック

19マニュアルの読み方「はじめに」「デザイナーのためのSmarty」「プログラマのためのSmarty」「付録」に大きく分かれているが、開発を進めていく上でよく使うのは「デザイナーのための〜」と「プログラマのための〜」の２つ。
デザイナーのためのSmarty
テンプレートに割り当てられた変数(テンプレート変数)の整形(修飾)や、テンプレート内で呼び出す関数などに関するマニュアル。
↓
テンプレートファイルの見栄え・整形に関するマニュアル
プログラマのためのSmarty
Smartyクラスのメンバ変数・メンバ関数の解説や、Smartyで使えるプラグイン・フィルタ・キャッシュなど拡張機能に関するマニュアル。
↓
Smartyクラスのインタフェイスや拡張機能などに関するマニュアル20

Smartyのインストール
解凍したフォルダ内の./libs/以下を適当なディレクトリに格納したら終了./libs/以下の内容はこんな感じ。
Smarty.class.php ※コレがSmartyの本体部分
Smarty_Compiler.class.php
Config_File.class.php
debug.tpl/internals/*.php/plugins/*.php

セキュリティ上の理由から、/libs/以下をウェブサーバのドキュメントルート以外、かつ全アプリケーションがアクセスするところに配置することが推奨されている。これはバージョンアップにも簡単に対応できることにも考慮してのこと。
もしも各アプリケーションにSmartyを個別にインストールしたければ、各アプリケーションが管理するディレクトリ下、かつドキュメントルート以外の場所にインストールしておけばいいだけの話。
21

各アプリケーション毎のセットアップ
インストール後、Smartyを利用するためには各アプリケーション毎のセットアップが必要
zSmarty.class.phpにパスを通す
zアプリケーション用ディレクトリの作成

22

各アプリケーション毎のセットアップ１【 Smarty.class.phpにパスを通す】
Smarty.class.phpをrequire()して呼び出す必要があるので、このファイルをどうにかして読めるようにする↓
その方法は？
z include_pathにSmartyインストールディレクトリを追加する
z require()で呼び出す時にSmarty.class.phpを絶対パスで指定する

上記以外にも定数SMARTY_DIRにインストールディレクトリを定義する手法があるが、通常は上記のどちらかをやれば問題ないのであまり気にしないでいいような気がする。詳しく知りたい人はマニュアルの「はじめに」あたりを参照。

23

各アプリケーション毎のセットアップ２【アプリケーション用ディレクトリの作成】
下記4つのディレクトリを、アプリケーションごとに用意する。
z templates
z templates_cz configs
z cacheテンプレートファイル格納ディレクトリ
コンパイル済みテンプレートファイル格納ディレクトリ
設定ファイル格納ディレクトリ
キャッシュファイル格納ディレクトリ

「templates_cディレクトリ」および「cacheディレクトリ」の2つは、ウェブサーバの権限で書き込み可能に設定しておく。なお、これらディレクトリもSmarty本体と同じく、ウェブサーバのドキュメントルート外に設置するのを推奨

24これにてセットアップ終了
25

セットアップ終了後のディレクトリ構成例
Smarty(Smarty.class.phpなど)のインストール先はここ
ドメイン別のアプリケーション格納先
www.example.comのドキュメントルート
www.example.comでSmartyが利用するディレクトリ群(アプリケーション毎に用意する)

26

Hello, World!
Index.php：

Index.tpl：<?php<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN“"http://www.w3.org/TR/html4/loose.dtd"><html lang="ja">// Smartyライブラリを読み込む
require('Smarty.class.php');// Smartyオブジェクトのインスタンスを作成$smarty =& new Smarty;// アプリケーション用ディレクトリを初期化$smarty->template_dir = '/web/www.example.com/smarty/templates/';$smarty->compile_dir = '/web/www.example.com/smarty/templates_c/';$smarty->config_dir = '/web/www.example.com/smarty/configs/';$smarty->cache_dir = '/web/www.example.com/smarty/cache/';<head><meta http-equiv="Content-Type" content="text/html; charset=euc-jp"><title>{$message}</title></head><body><h1>{$message}</h1></body>// テンプレート変数$messageに値を割り当て$smarty->assign('message','Hello, World!');</html>// テンプレートファイル「index.tpl」を使って画面表示$smarty->display('index.tpl');?>27

ソースレベルでの処理の流れ(プログラマ側)
Smartyのインクルード<?php// Smartyライブラリを読み込む
include "Smarty.class.php";

Smartyインスタンスの生成// Smartyオブジェクトのインスタンスを作成$tmpl =& new smarty;// アプリケーション用ディレクトリを初期化$tmpl->template_dir = '/web/www.example.com/smarty/guestbook/templates/';$tmpl->compile_dir = '/web/www.example.com/smarty/guestbook/templates_c/';$tmpl->config_dir = '/web/www.example.com/smarty/guestbook/configs/';$tmpl->cache_dir = '/web/www.example.com/smarty/guestbook/cache/';アプリケーション用
ディレクトリの初期化 ※// テンプレート変数$messageに値を割り当て$smarty->assign('message','Hello, World!');// テンプレートファイル「index.tpl」を使って画面表示$smarty->display('index.tpl');?>テンプレート変数を割り当て表示※smarty継承クラスを作成し、コンストラクタで設定を済ませるのがセオリー

28

ソースレベルでの処理の流れ(デザイナ側)<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN“"http://www.w3.org/TR/html4/loose.dtd"><html lang="ja"><head><meta http-equiv="Content-Type" content="text/html; charset=euc-jp"><title>{$message}</title></head><body><h1>{$message}</h1>

普通にHTMLをコーディング
動的変更が必要な部分で、PHPから渡されたテンプレート変数を呼び出し、好きなように整形して表示</body></html>29

拡張セットアップ
このまま進めても問題はないけれど、アプリケーション用ディレクトリの初期化を毎回するのは面倒。なのでSmarty継承クラスをつくり、アプリケーション用ディレクトリ初期化などの共通処理をコンストラクタで済ませるようにする。
mysmarty.php：
require_once 'Smarty.class.php';/*** Smarty初期化クラス*/
class MySmarty extends Smarty {/*** コンストラクタ*/
function MySmarty (){/*** Smartyクラスのコンストラクタを呼び出す*/$this->Smarty();$basedir = ‘/web/www.example.com/smarty/’;$this->template_dir = $basedir . 'templates/';$this->compile_dir = $basedir . 'templates_c/';$this->config_dir= $basedir . 'configs/';$this->cache_dir= $basedir . 'cache/';
}
}

30

最低限
知っておけ！
プログラマ編31

プログラマは知っとけ その１【Smartyクラス変数】$template_dir
z $compile_dir
z $config_dir
z $cache_dir
z $left_delimiterz $right_delimiterz $debuggingz// テンプレートファイル格納先// コンパイル済みファイル格納先// 設定ファイル格納先// キャッシュファイル格納先// デリミタ開始文字列// デリミタ終了文字列// デバッグフラグ(後述)32プログラマは知っとけ その２【Smartyクラス関数】
assign()z config_load()z display()z fetch()z// テンプレート変数割り当て// 設定ファイル読み込み// 出力結果表示// 出力結果取得

33

最低限
知っておけ！
デザイナ編
34

デザイナは知っとけ その１【デリミタ】{ と } で囲むと、Smarty特有の処理開始と終了を表す。
Ex.
{* コメントだったり *}{$var}{section name=I loop=$var}
・・・{/section}

35

デザイナは知っとけ その２【コメント】
デリミタ( { } )と * で囲む。
Ex.
{* ここはコメント *}{*こんなのもコメント*}{*******************************************************************************
これだってコメントだ*******************************************************************************}36

デザイナは知っとけ その３【テンプレート変数】
PHPから表示用に渡される変数のこと。bool、int、string、配列、オブジェクトなど、PHPで変数として扱えるものは全てテンプレート変数として渡すことができる。テンプレート変数$varへのアクセス方法はこんな感じ。

{$var}
Ex.
phpファイル側：$smarty->assign(ʻvarʼ, ‘ふー’); // これでテンプレート変数$varに「ふー」を割り当てたことになる
テンプレートファイル側：{$var} ←これが「ふー」に置き換わって表示される

37

デザイナは知っとけ その４【必修のテンプレート変数】
連想配列：

予約変数$Smarty：{$var.id} ←「.」の後にキーを書く

get, post, cookies, server, environment, sessionのようなリクエスト変数にアクセスする。その他、{$smarty.now}で現在時刻へアクセスしたりできる。

配列のインデックス：{$var[6]} ←PHPと同じオブジェクト：{$var->id} ←PHPと同じ

設定ファイルからの変数：{#var#} もしくは {$smarty.config.var}

例外な変数：{$SCRIPT_NAME} は{$smarty.server.SCRIPT_NAME}と同じ$_GET[‘name’]へアクセス{$smarty.get.name}$_POST[‘id’]へアクセス{$smarty.post.id}$_SESSION[‘cart’]へアクセス{$smarty.session.cart}$_ENV[‘PATH’]へアクセス{$smarty.env.PATH}$_SERVER[‘REQUEST_URI’]へアクセス{$smarty.server.REQUEST_URI}38

デザイナは知っとけ その５【修飾子】
テンプレート変数を整形(修飾)するもの。複数の修飾子を「|」で連結することもできる。テンプレート変数$varに修飾子modifierを適用する場合はこんな感じ。

{$var|modifier}{$var|modifier:param1:param2・・・}
Ex.
{* タグを取り除くstrip_tags修飾子を$valueに適用 *}{$value|strip_tags}{* 修飾子を連結 *}{$name|default:’ゲスト’|escape:’htmlall’}{* 現在日付をyyyy/mm/dd形式で表示 *}{$smarty.now|date_format:’%y/%m/%d’}39デザイナは知っとけ その６【必修の修飾子】// デフォルト値を設定
zescape// 各種エスケープ
zdate_format// 日付フォーマット整形
zstring_format// 文字列フォーマット整形
znumber_format // 数値をカンマ区切りに整形
zdefault

なお、全てのPHP関数は「暗黙で」修飾子として使うことが可能。ただし引数が多い関数の場合は、自作プラグインを作らないとわかりにくくなる傾向がある。
Ex. 3つのパラメータをもつPHP関数fooを「暗黙の」修飾子として使うと・・・
function foo(param1, param2, param3)↓{param1|foo:param2:param3･･･}
40

デザイナは知っとけ その７【組み込み関数】
テンプレート内で条件分岐(if)を行ったり、繰り返し処理(section)を行ったり。覚えればかなり強力。if文はこんな感じ。

{if $var==true}〜{/if}Ex.
{* $nameが設定されていたらウェルカムメッセージを表示 *}<div class=“message”>{if $name!=‘’}<p>ようこそ {$name|escape:’html’} さん</p>{else}<p>ログインしてください</p>{/if}</div>
41

デザイナは知っとけ その８【必修の組み込み関数】
z {if}〜{elseif}〜{else}〜{/if}z {section}〜{sectionelse}〜{/section}z {include}z {config_load}z {literal}〜{/literal}42

デザイナは知っとけ その９【カスタム関数】
テンプレート内で新たにテンプレート変数を作ったり、計算をしたり。組
み込み関数よりは細々とした機能のものが多い。カスタム関数customの属性varにparam1を渡すときはこんな感じ。

{custom var=param1}
Ex.
{* テンプレート変数$nameを作成 *}{assign var="name" value="Bob"}{* 5000×(100+5)/100を計算し、その結果をテンプレート変数$allに格納 *}{math equation=‘price*(100+tax)/100’ price=$price tax=$tax assign=“all”}

43

デザイナは知っとけ その１０
【必修のカスタム関数】
z {assign}z {math}z {html_checkboxes}系

44

デザイナは知っとけ その１１
【テンプレート内で｛と｝を文字列として使う】
例えば、Javascript でfunction foo () { alert(‘foo’); }とか、CSSで
h1 { font-size: 120%; }とかをテンプレートファイルに直接書きたいときはどうするか？
zzz

外部ファイル化する(.jsファイルや.cssファイルへ)
デリミタを{ } 以外のものに変更する(PHP側で対処){literal}〜{/literal}で囲む(テンプレート側で対処)
45

デザイナは知っとけ その１２
【その他の決まりごと】
zz

デリミタ内で文字列【テンプレート変数、真偽値(true/false、yes/no、on/off)、数値以外】を扱いたい場合は“(ダブルクォート)もしくはʻ(シングルクォート)で囲む必要がある。
“(ダブルクォート)内にテンプレート変数を含みたい場合は、そのテンプレート変数が数字、文字、アンダーバー、[]のみで構成されていればそのまま書けるが、これら以外の文字列(.とか->とか)がある場合は`(バッククォート)で囲む必要がある。詳しくはマニュアル「基本構文」を参照。

46

真骨頂のキャッシュ機能
さわりだけ
47

キャッシュって何よ前回リクエストされた際のHTMLを一定時間保存して(キャッシュして)おき、次回以降のリクエストではassign()など動的処理の多くをすっ飛ばして高速化を図る
z 当然サーバの負荷が軽くなるz 毎回同じ商品データをDBから取得して一覧表示しているようなページや、アクセスが多いわりに更新頻度が低いページ(トップページやRSSなど)に使うと効果的
z キャッシュが有効でも、一部を動的に変えるという芸当もできる(「ようこそｘｘｘさん」部分だけは毎回動的に変えたりとか)
z コンパイルとは違うのだよ、コンパイルとはz48

ＰＨＰ側でソースを修正するだけでも使えたりして便利<?php$tmpl =& new MySmarty();$tmpl->caching=true;
if (!$tmpl->is_cached()) {$tmpl->assign('abc', $abc);$tmpl->assign('def', $def);
・・ // フェッチしたりアサインしたり・$tmpl->assign('xyz', $xyz);

キャッシュされていれば、ここの処理は全てスキップできる}$tmpl->display('index.tpl', md5($_SERVER['REQUEST_URI']));?>
49

詳しくは下記参照
PHP関西セミナーでSmartyキャッシュに関する講演をされた方(ushiro様)が、その際の資料をアップされています。感謝。とてもわかりやすいです。
『Syrup Factory』
http://syrup-factory.com/『Linuxを使ってみる』
http://www.syrup-factory.com/b/linux/『2005年06月02日 「Smarty Cacheは難しくない」の資料』
http://www.syrup-factory.com/b/archives/2005/06/smarty_cache_1.php

50

最後に
いろいろと
51

２バイト文字を考慮して作られていないことを認識する
truncate修飾子など、指定したキャラクタ数でああするこうする、といったものは、２バイト文字の扱いが考慮されていない。
そのため、日本語ベースでこれらの機能を使いたければ、現状ではプラグインを作るしかない。

52

セキュリティを十分に考慮する
ユーザによって入力された値を表示する際には、必ずescape修飾子を用いること。XSSの脆弱性を引き起こす可能性がある。
$default_modifiersに
escape:’htmlall’ (htmlentities相当)
もしくはescape:’html’ (htmlspecialchars相当)
を入れておくのも一つの手。(htmlのがいいかも？)53

デバッグ機能を使う$smarty->debugging=trueと設定したSmartyインスタンスでdisplay()を呼ぶと、全てのテンプレート変数に割り当てられた値をポップアップウィンドウで一覧することができる。ただしfetch()の時は使えないので注意。
なお、ブラウザ側でポップアップを禁止している場合には解除しないと出てこないので、ポップアップしなければ一度ブラウザ設定を見直すこと。

54

今回触れてないけど結構重要なこと
z独自の修飾子やカスタム関数の作成方法
zプリフィルタ・ポストフィルタ・アウトプットフィルタ
zキャッシュの詳細な使い方とか身につけておくと何かと心強いかと思います。

55

関連書籍のご紹介『極める ! PHP』 ￥1,764
坂井 恵 (著), 坂井 恵, 上鍵 忠志, 田中 正裕, 月宮 紀柳, 森川 穣
翔泳社 ; ISBN: 4798108766 ; (2005/06/02)『Smarty入門~PHP5＋テンプレート・エンジンでつくるMVCアプリケーション~ 』￥2,940
山田 祥寛 (著)
翔泳社 ; ISBN: 4798108839 ; (2005/03/15)『まるごとPHP!〈Vol.1〉』￥1,995
山田 祥寛 (著), 桝形 誠二 (著), 広川 類 (著), 山本 勇 (著), 岩切 洋一 (著)
インプレス ; ISBN: 4844320254 ; Vol.1 巻 (2004/09)

56

ご静聴
ありがとうございました57