高機能なPHPテンプレートHTMLエンジン、Smartyについての2005年6月当時のメモです。当時はバージョンが2.6.6とかその辺を使ってました。2008年12月現在、大分マイナーバージョンが上がっていますが、基本的な使い方やセキュリティ面でのTipsは今でも役に立つと思います。
公式サイト: http://www.smarty.net/
公式日本語マニュアル: http://www.smarty.net/manual/ja/
公式日本語マニュアル参照: http://www.smarty.net/manual/ja/
実際の使い方のノウハウは、Smartyを使っているCMSアプリのコードを調べてみることをお奨めします。自分もXOOPSのコードで色々勉強しました。(*1)
また
Smarty-2.6.x/demo/
以下にサンプルが入っていますので、そちらも忘れずに目を通してみましょう。
LinuxやUNIXユーザの場合、templates_cディレクトリをWebサーバ実行ユーザーから書き込み可能にしておくのを忘れずに。
$ chmod 777 templates_c/
とでも実行しておいてください。
Smartyのコマンドタグは、"{"で始まり"}"で終わるのがデフォルトです。しかし、これだとJavaScriptの関数と同じなので、不便です。
そこで、XOOPSやYakiBikiでは次のようにして、"<{"と"}>"に変更しています。
$smarty = new Smarty; $smarty->left_delimiter = "<{"; $smarty->right_delimiter = "}>";
XOOPSのコードを見てみましょう。
xoops-2.X.X/html/class/smarty/plugins/resource.db.php
上記ファイルが参考になるはずです。
(2005年当時のファイルになります。2008年現在、XoopsCubeになってどうなっているのかは分かりません、御免なさい。)
データベースを利用して、テンプレートをユーザーが自由に変更できるようなCMSを作成する場合、絶対に注意しなければならない事項があります。
Smartyのデフォルトでは {php} - {/php} 組み込み関数により、自由にPHPコードを記述できます。 これはすなわちユーザーに、PHPでできるありとあらゆる操作を行える力を与えることに他なりません。
JavaScriptを埋め込まれる以上に危険なこの機能を無効化するには、 $securityプロパティをtrueにします。
$smarty->security = true;
この一行を絶対に忘れないでください。
この場合でもし{php}タグが使われていた場合、Smartyはエラーを出力すると同時に処理も続行します。
ここも2005年当時の話です。アップデートできず御免なさい。2008年現在は、公式日本語マニュアルの「第15章 拡張機能」→「オブジェクト」の項を参照して下さい。
オブジェクトを割り当てる場合、
register_object assign(_by_ref)
の二種類が存在します。
register_objectを使うと、第三パラメータに使用可能なメソッド・メンバ名を配列で指示できます。
assign(_by_ref)では、以下の二つの条件すべてが成立している場合に、"_"で始まるメンバ・メソッドへの(テンプレートHTML内での)アクセスを禁止できます。(Smarty errorが発生します。)
register_object()に関しては、この"_"で始まる要素へのアクセス制限機能は適用されないことに注意してください。
このオブジェクト割り当てを調べたときの過程をメモっておきます。
まず、register_object でSmartyのソースツリー(正確にはlibs/以下)をgrepしました。そしたら、Smarty.class.phpの
function register_object($object, &$object_impl, $allowed = array(), $smarty_args = true, $block_methods = array()) { settype($allowed, 'array'); settype($smarty_args, 'boolean'); $this->_reg_objects[$object] = array(&$object_impl, $allowed, $smarty_args, $block_methods); }
にヒットしました。んで、これ見ると_reg_objects[]という配列にいろいろセットされてます。実際にregister_objectで割り当てたオブジェクトを使用しているテンプレートHTMLのコンパイル後の該当部分を見てみると、
<{obj1->meth1 ... }>
が、
<?php echo $this->_reg_objects['obj1'][0]->meth1(array(), $this);?>
になってました。
先の調査結果をふまえて、_reg_objectsでgrepしたら、Smarty_Compiler.class.php中で
function _compile_registered_object_tag($tag_command, $attrs, $tag_modifier)
が見つかりました。んで、調べていくと・・・
} elseif( !empty($this->_reg_objects[$object][1]) && !in_array($obj_comp, $this->_reg_objects[$object][1])) { $this->_trigger_fatal_error( "'$obj_comp' is not a registered component of object '$object'", $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
というのが見つかりました。$obj_compというのはこの関数の前の方で
list($object, $obj_comp) = explode('->', $tag_command);
として取得されています。つまりメンバ・メソッド名の部分ですな。
というわけで、register_object()の場合は $smarty->securityとは何の関係もないどころか、"_"ではじまるプライベート要素にはアクセスし放題というわけです。
だから代わりに、register_object()の第三パラメータの配列でアクセス制限をかけている訳ですね。(代わりというわけではないと思いますが。)
'_'でSmarty_Compiler.class.phpを検索したら見つかりました。_parse_varとかいう関数の中で、
} else if (substr($_index,0,2) == '->') { if(substr($_index,2,2) == '__') { $this->_syntax_error( 'call to internal object members is not allowed', E_USER_ERROR, __FILE__, __LINE__); } elseif($this->security && substr($_index, 2, 1) == '_') { $this->_syntax_error( '(secure) call to private object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
みたくなってました。