2010年7月13日火曜日

Twigでタグを自作

Twigで新しいタグを定義してみました。

タグを自作して、それをTwigに組み込む方法が
少し分かりました。


が、想像以上に面倒ですし、
今でも未だ分からないことがたくさんです。。。



0.概要

(1)作りたいタグ
今回、Twig0.9.8で、次のようなタグを作ってみます。
    {% sample name as 'my tag' %}

一応、ドキュメントでは当該タグの作り方が記述されてますが、
どうやら内部仕様が変わってしまったようでして、
ドキュメント通りにコーディングしても、うまく動作しません。。。

そこで、実際どうやるの?っていうのを調べてみます。


(2)作成手順
以下を見てもらえれば分かるんですが、
大まかに言うと、タグを新しく作るには、
 ■TokenParser:構文解析し、Nodeを生成する
 ■Node:コンパイル時に出力する内容を生成
の2点を新しく作成する必要があるようです。

そして、それらは、
 ■Extension
   ↓
 ■TokenParser
   ・
   ・(コンパイラによるコンパイル)
   ・
 ■Node
のような手順で実行されるようです。



1.Extension定義

filterやtagを登録するのは、基本的に、
Twig_Extensionを継承したクラスから行います。
ここでは、自作したTokenParserを登録します。
<?php
class Twig_Extension_Sample extends Twig_Extension
{
    // !! return a handmaid token-parser !!
    public function getTokenParsers()
    {
        return array(
            new Twig_TokenParser_Sample(),
        );

    }

    public function getName()
    {
        return 'sample';
    }
}

?>




2.TokenParser作成

TokenParserを作成するとき、一応、parserの機能を使ってparseできます。
しかし、具体的に、どんな機能があるかは、
Twig_Parserクラスのソースを読むしかないようです。。。
<?php
class Twig_TokenParser_Sample extends Twig_TokenParser
{
    public function parse(Twig_Token $token)
    {
        $lineno = $token->getLine();

        $stream = $this->parser->getStream();
        $exp = $this->parser->getExpressionParser();

        // !! analyze token !!
        $name = $exp->parseAssignmentExpression();
        $stream->expect(Twig_Token::NAME_TYPE, 'as');
        list(, $value) = $exp->parseMultitargetExpression();
        $stream->expect(Twig_Token::BLOCK_END_TYPE);

        return new Twig_Node_Sample($name, $value, $lineno, $this->getTag());
    }

    // !! this is tag name (in this case, 'sample' tag) !!
    public function getTag()
    {
        return 'sample';
    }

}
?>




3.Node作成

ここでの目玉は、何といってもcompile()ですが、
ここもTwig_Compilerクラスのソースを読むしかないようです。。。
<?php
class Twig_Node_Sample extends Twig_Node
{
    protected $_name = null;
    protected $_value = null;


    public function __construct($name, $value, $lineno, $tag = null)
    {
        parent::__construct(array('name' => $name, 'value' => $value), array(), $lineno, $tag);

        $this->_name = $name;
        $this->_value = $value;
    }

    public function compile($compiler)
    {
        $compiler->subcompile($this->_name, false);
        $compiler->raw(' = ');
        $compiler->subcompile($this->_value);
        $compiler->raw(";\n");
    }

}
?>




これらにより、実際のコンパイル結果には、
次のように出力されます。
$context['name'] = "viva";

たったこれだけ出力するだけで、結構な労力が必要ですね。。。

タグを自作する場合には、どうしても必要な場合だけに
限ったほうがいいと思います

でも、どうしても必要な場合は、果たしてあるんでしょうか・・・?
それよりも、Twigを使う場合のViewクラスなどを工夫したほうが
よさそうな気がしますが、どうでしょう?

0 件のコメント:

コメントを投稿

どうかお気軽にコメント頂ければ幸いです。