Smartyのテンプレートの文字列内変数を拡張する
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自体を大幅に改変する必要がありそうです。興味のある方は挑戦してみてください。