注文の多いXOOPS屋 TOP現在のブログ旧スタッフblog アーカイブ
このページは 2007〜2008 年に公開していた「株式会社RYUS スタッフblog」のアーカイブです。内容は当時のものです。

Smartyのテンプレートの文字列内変数を拡張する

2008/04/07 17:20   sato   カテゴリ: XOOPS Cube, development, PHP

satoです。
XOOPSでは、テンプレートエンジンにSmartyを使うことになります。
このSmartyは便利なのですが、若干使いづらい部分もあったりまします。

まず前提として、Smartyの文字列定数中に変数を混ぜたい場合、バッククォート(`)で囲むとそれが変数として解釈されるようになっています。
例えば、


{\"私は`$name`です。\"}

として、$nameに\"sato\"という文字列がassignされていると、

私はsatoです。

と表示されます。

しかし、このバッククォート内にメンバ関数を指定すると、解釈がうまくいかないようです。

例えば、


私は{$my->show(\'name\')}です。

みたいなものは可能なのですが、

{\"私は`$my->show(\'name\')`です\"}

とすると、

私は`Object id #2->show(\'name\')`です。

のように、$myの部分だけ変数として解釈され、それ以外は普通の文字列として出力されてしまいます。
通常、こういう場合は、

{assign var=name value=$my->show(\'name\')}
{\"私は`$name`です\"}

のように1段階assignを追加することになり、若干手間がかかります。
今回はこれの簡易的な対応方法を試してみました。

Smartyのassignされた変数というのは、実際にそのスコープに存在するものではなく、Smartyクラスの_tpl_varsメンバ変数に格納されています。このため、テンプレート側で記述した変数は、Smartyの構文解析を通してSmartyクラスのメンバ変数になるように置換処理が入ります。具体的には生成されたテンプレートキャッシュを見ればわかると思います。
そのため、Smartyでは正規表現を利用して構文解析を行っているのですが、この正規表現に手を入れることで、上記の対応が可能になります。

バッククォート内を解釈しているのは、Smarty_Compiler#_expand_quoted_textで、変数の解析をしている正規表現は_obj_ext_regexpに格納されています。
このため、Smarty_Compilerに手を入れればいいのですが、Smarty_CompilerはSmarty#_compile_sourceで生成され、すぐ破棄されるので、途中からアクセスすることはできません。Smarty_Compiler.class.phpを直接書き換えればいいという話もありますが、それでは汚くなってしまいます。
幸い、Smartyクラスが生成するSmarty_Compilerクラスは別のものに変更ができるようになっています。Smartyクラスのcompiler_fileとcompiler_classにそれぞれ読み込むファイルとクラス名が格納されているので、それを自前のものに変更します。

具体的にやってみます。
以下のファイルを作成します。パス等は適宜変更してください。
MySmartyCompiler.class.php


require \'Smarty-2.6.19/libs/Smarty_Compiler.class.php\';

class MySmartyCompiler extends Smarty_Compiler
{
function MySmartyCompiler()
{
parent::Smarty_Compiler();

$this->_obj_ext_regexp = str_replace(\'(?:\\[\\$?[\\w\\.]+\\])*\', \'(?:\\[(\\$?[\\w\\.]+)\\])*(?:\\((\\$?[\\w\"\\\'\\.]+)\\))*\', $this->_obj_ext_regexp);
}
}

呼び出し側を作成します。
index.php


require \'Smarty-2.6.19/libs/Smarty.class.php\';

class myClass {

function show($var) {
return \'sato\';
}

}

$smarty = new Smarty();
$smarty->compiler_file = \'MySmartyCompiler.class.php\';
$smarty->compiler_class = \'MySmartyCompiler\';

$smarty->template_dir = \'templates\';
$smarty->compile_dir = \'templates_c\';
$smarty->config_dir = \'config\';
$smarty->cache_dir = \'cache\';
$smarty->plugins_dir = \'plugins\';

$smarty->assign(\'my\', new myClass());
$smarty->display(\'index.tpl\');

テンプレートを作成します。
templates/index.tpl


<html>
<body>

{\"私は`$my->show(\'name\')`です。\"}

</body>
</html>

実行すると、


私はsatoです。

のように正常に表示されているのが確認できると思います。
全てのパターンでテストを行っているわけではないので、不具合があったらコメント欄等でよろしくお願いします。

XOOPSからこれを利用する場合は、各RenderSystem内に存在するSmartyオブジェクトを取得して書き換える必要があります。
モジュールの場合は通常はLegacy_RenderSystemなので、


$root =& XCube_Root::getSingleton();
$renderSystem = $root->mContext->mModule->getRenderSystem();
$renderSystem->mXoopsTpl->compiler_file = \'MySmartyCompiler.class.php\';
$renderSystem->mXoopsTpl->compiler_class = \'MySmartyCompiler\';

のような感じで対応が可能です。(表示処理の実行前に記述してください)
管理画面はLegacy_AdminRenderSystemなので、mXoopsTplではなくmSmartyを参照することになります。

しかしこの方法でも、


{\"私は`$pattern[$foo->bar()]`です\"}

のようなパターンには対応できません。このパターンに対応するには_expand_quoted_text自体を大幅に改変する必要がありそうです。興味のある方は挑戦してみてください。