PHPのhtmltemplate.incでオブジェクトやらフィルターやら

ひょんなことからhtmltemplate.incというHTMLテンプレートエンジンを触ることになった。

どんな感じのテンプレートなのかは

あたりで詳しく書かれている。

Smartyに慣れている人間からすると非常に使いにくい。オブジェクトが渡せなかったり、テンプレート内でHTMLエスケープ等のフィルター処理ができなかったりする。

で機能追加をしたいわけなのですが、これがなかなかhtmltemplate.incというやつは短いコードながら設計が優れていてとても機能拡張がしやすい。

というわけでやっちゃいました。

<?php
require_once 'htmltemplate.inc';

class FilterTag extends DataTag {
    function getIndex($m,$multilabels){
        $code = preg_split ('/\|/',$m);
        $val = array_shift($code);
        if ( preg_match('/^([\w\/]+)(.*)$/',$val,$matches) ) {
            $ind = parent::getIndex($matches[1],$multilabels);
            if ( isset($matches[2]) ) $ind .= $matches[2];
            $ind = "\$val$ind";
        }
        else {
            $ind = "\$val".parent::getIndex($val,$multilabels);
        }
        if ( count($code) ) {
            foreach ($code as $filter) {
                $args    = preg_split ('/\:/',$filter);
                $filname = array_shift($args);
                if ( count($args) ) {
                    $arg = join(',',$args);
                    $ind = "htmltemplate_filter_$filname($ind,$arg)";
                }
                else {
                    $ind = "htmltemplate_filter_$filname($ind)";
                }
            }
        }
        return $ind;
    }
}

class tag_fil extends FilterTag {
    var $matchregexp='/\{fil ([^\}]+)\}/i';
    var $fromstring="{fil %s}";
    var $tostring="<?php @print %1\$s; ?>\n";
}

/**
 * HTMLエスケープして返すフィルター
 * {fil foo|html}
 */
function htmltemplate_filter_html ($str) {
    return htmlspecialchars($str);
}

/**
 * 改行コードを<br />に変換して返すフィルター
 * {fil foo|nl2br}
 */
function htmltemplate_filter_nl2br ($str) {
    return nl2br($str);
}

// シングルトンクラスなのでどこから呼び出してもOK
$html =& htmltemplate::getInstance();

// 機能追加!
$html->parser->add(new tag_fil);

これで機能追加完了。filというタグを追加しました。

<?php

$val = array('foo' => '<hr>');
print htmltemplate::buffer("A{fil foo|html}B",$val); // A&lt;hr&gt;B

となります。

filタグの機能は下記のような感じ

 {fil foo}            // rvalと同じ
 {fil foo/0}          // 配列呼び出し
 {fil foo[0]}         // 配列呼び出し
 {fil foo()}          // 関数呼び出し
 {fil foo->bar()}     // オブジェクト呼び出し
 {fil foo|html}       // HTMLエスケープフィルターを呼ぶ
 {fil foo|html|nl2br} // 複数のフィルターを重ね掛けすることも可能
 {fil foo|bar:100}    // フィルターに引数を渡すことも可能

実際にそれぞれの動きを見てみよう。

<?php

function htmltemplate_filter_plus ($str,$num) {
    return $str+$num;
}
class Foo { function bar () { return 'bar'; } }

$val = array(
    'foo1' => '<hr>',
    'foo2' => array('aaa'),
    'foo3' => array('bbb'),
    'foo4' => 'rand',
    'foo5' => new Foo,
    'foo6' => '<hr>',
    'foo7' => "<hr>\n",
    'foo8' => 50,
);

print htmltemplate::buffer("
 {fil foo1}
 {fil foo2/0}
 {fil foo3[0]}
 {fil foo4()}
 {fil foo5->bar()}
 {fil foo6|html}
 {fil foo7|html|nl2br}
 {fil foo8|plus:100}
",$val);
$ php test.php
 <hr>
 aaa
 bbb
 21871
 bar
 &lt;hr&gt;
 &lt;hr&gt;<br />

 150

うまく動いてます。

なんせ古いクラスなので需要があるのかさっぱりわからないけど何かの参考になれば幸いです。