2010年5月8日土曜日

【Twig】テンプレートにassignした変数を取得

あらかじめ

 {{ foo1 }}とか

 {{ foo2|safe }}とか

 {{ foo3.func('bar') }}とか

などと書かれたテンプレートを解析して、

foo1とかfoo2とかfoo3を抽出したい、というような場合。

※需要、ないなあ・・・。



パッと思いつくのはpreg_matchとかで

文字列解析する方法ですが、

テンプレートエンジンの場合、

タグの開始・終了文字(*1)をユーザが自由に

変更できたりするなど、

あまり単純な方法ではダメなのではないか、と思い。



でも、twigの場合、parser,lexerが外から操作できたり

簡単に拡張できたりするので、それでやってみよう、と。



・・・ということをやってみました。



抽出する関数
function getAssignVarsFrom($stream)
{
$ret = array();
$bIn = false;
$bInFirst = false;
while(! $stream->isEOF())
{
// get current node
$curr = $stream->getCurrent();

switch ($curr->getType()) {
case Twig_Token::VAR_START_TYPE:
$bIn = true;
$bInFirst = true;
break;
case Twig_Token::NAME_TYPE:
if ($bInFirst) {
$ret[$curr->getValue()] = $curr->getLine();
}
$bInFirst = false;
break;
case Twig_Token::VAR_END_TYPE:
$bIn = false;
$bInFirst = false;
break;
default:
break;
}

// next
$stream->next();
}
return $ret;
}



呼び出し側
$loader = new Twig_Loader_Filesystem('/path/to/template/');
$twig = new Twig_Environment( $loader,
array(
'cache' => '/path/to/cache',
'auto_reload' => true,
)
);
$escaper = new Twig_Extension_Escaper(true);
$twig->addExtension($escaper);
$stream = $twig->tokenize($twig->getLoader()->getSource('sample2.html'), 'sample2.html');
$assigned_vars = getAssignVarsFrom($stream);

※かなり手抜きです。
 "Twigの使い方"みたいなヤツのサンプルを簡単にイジっただけなので、
 余計な処理がテンコ盛りだと思います。
 (コンパイルしてないからcacheいらないんじゃないか、とか。
  extenssionは関係ないんじゃないか、とか。)



tokenize()が返してくるstreamの中にtokenが格納されているので、

NAME_TYPEだけ引っこ抜く、と。



それだけだと、safeとかfuncだとかも引っこ抜いてきてしまうので、

取り敢えず、VAR_START_TYPEからVAR_END_TYPEの間で

最初に出てきたNAME_TYPEだけ引っこ抜く、と。



これで本当によいのかどうか、かなり怪しいところですが、

取り敢えず、ちゃんとfoo1,foo2,foo3だけ返してきてます。



ちなみに、Twigのバージョンは、0.9.6-DEVを使いました。

2010年5月6日木曜日

Twig?

PHP向けテンプレートエンジンである、Twig。

その出自から、symphonyというフレームワークと

相性がいいとは思うのですが、

別にsymphonyでなくとも使いたいなあと思い、

最近、少し研究しています。



サンプルを見ていると、

変数をassignしてrenderする方法はたくさん見つかりますが、

クラスのインスタンスをassignして

テンプレート側でクラスメソッドを実行するのって

あまりサンプルを見かけることがなかったのですが、

直感的に出来るハズだと思い、やってみました。


呼ばれるクラス。
class vivatest
{
    public function find($param = 0)
    {
        echo "this is vivatest's find method. ( $param ) <br />";
    }
}



クラスのインスタンスをテンプレートに埋め込む処理。
    $testcls = new vivatest();

(・・色々、省略。詳しくは、Twigのサイトでサンプル拾ってください・・)

    $template = $twig->loadTemplate('samplehtml');
    echo $template->render(array('vivatest' => $testcls));



テンプレート。
<html>
<title>title</title>
</html>
<body>
<p>{{ vivatest.find() }} {{ vivatest.find(1) }} {{ vivatest.find('x') }}</p>
</body>



実行結果。
this is vivatest's find method. ( 0 )
this is vivatest's find method. ( 1 )
this is vivatest's find method. ( x )




・・・別に、何のことはなかったですね。



直感的に「こうするんじゃないかな?」と思った方法で、

ちゃんと実行することが出来ました。

2010年5月4日火曜日

log4phpのLoggerConfiguratorPhp.php

LoggerConfiguratorPhpでLoggerAppenderRollingFileを使えるよう、
修正してみました。
@@ -85,8 +85,6 @@
                    
                }
                
-                // added by viva(2010.05.04)
-                $this->_validateApender($appender, $appenderProperties);
            }
            
        }
@@ -131,21 +129,6 @@
        }
        
        return true;
-    }
-
-    //
-    // added by viva(2010.05.04)
-    //
-    protected function _validateApender($appender, $appenderProperties)
-    {
-        $validater = new LoggerReflectionUtils($appender);
-        foreach ($appenderProperties as $pk => $pv) {
-            if ($pk == 'class' || $pk == 'layout') {
-                continue;
-            }
-            $validater->setProperty($pk, $pv);
-        }
-        $validater->activate();
    }
    
}


定義ファイルは次のようにPHP配列の形で書きます。
<?php
return array(
        'threshold' => 'ALL',
        'rootLogger' => array(
            'level' => 'INFO',
            'appenders' => array('default'),
        ),
        'loggers' => array(
            'dev' => array(
                'level' => 'DEBUG',
                'appenders' => array('default'),
            ),
        ),
        'appenders' => array(
            'default' => array(
                'class' => 'LoggerAppenderRollingFile',
                'layout' => array(
                    'class' => 'LoggerLayoutPattern',
                    'conversionPattern' => "%d [%p]: %m (remote_ip_address=[%X{ip_address}] uri=[%X{uri}])%n",
                ),
                'file' => 'logging.log',
                'MaxFileSize' => '100k',
                'MaxBackupIndex' => 3,
            ),
        ),
    );
?>

LoggerConfiguratorPhpを使って便利なのは、
たとえばlog出力先を絶対パス指定や
"../hoge/"のようなあまり使いたくない形式の相対パス指定で
書かずに、"$logpath/log.txt"のように変数指定できるので、
設定を動的に変更することが容易になる
ことが挙げられます。


ちなみに、呼び出しは、次のようにします。
        LoggerMDC::put('ip_address', $_SERVER['REMOTE_ADDR']);
        LoggerMDC::put('uri', $_SERVER['REQUEST_URI']);

        Logger::configure('configurator_php.php', 'LoggerConfiguratorPhp');
        $logger = Logger::getRootLogger();
        $logger->info("logging start");


とは言え、大して需要があるとも思えませんので、
覚書程度に書き残している次第です。