2010年7月7日水曜日

Twigで自作フィルタ

Twigでは色々なものを自作して組み込むことができるそうで、
フィルタやタグは勿論のこと、何だったらparserとかも自作して
組み込むことができそうです。


今回、Twigのフィルタを自作して組み込んでみたので、
その方法について、軽く整理しておきます。


今回作成するのは、nl2br(改行コードをbrタグに変換)です。
信じられませんが、意外とこれが無かったりします。


1.フィルタ本体



class Sample_Extension extends Twig_Extension
{
    public function getFilters()
    {
        return array('nl2br' => new Twig_Filter_Method($this, 'nl2br'));
    }

    public function nl2br($value, $sep = '<br />')
    {
        return str_replace("\n", $sep."\n", $value);
    }

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

ポイントは2点。
 ■Twig_Extensionを継承して作り、getName()を実装
 ■フィルタとして登録する機能をgetFileters()で返す
  (このとき、PHP関数なのかクラスメソッドなのか
   インスタンスメソッドなのかで実装方法が異なりますので、
   詳しくはドキュメントを読みましょう。)

※ここでは、スゲー適当なクラス名で作成し、
 スゲー適当なファイル名で保存しました。

※Twig標準のautoloaderに読み込ませる場合には、
 真面目に命名し、真面目なファイル名で保存し、
 真面目なフォルダに保存しておきましょう。
 Twig/Extension/Core.phpや
 Twig/Autoloader.phpなど
 のソースを読めば、すぐに理解できると思います。

 

2.Twigへの組み込み



$twig->addExtension(new Sample_Extension());

上記1で作成したフィルタを、Twig_EnviromentにaddExtensionで追加します。



・・・以上です。

簡単でしたね。

合格証受領

合格証が届きました。

先週土曜日、所用で東京に行っていたのですが、

そのときに配送されていたようです。

第18回ITコーディネータ試験に合格しました。



8月下旬頃から研修が始まりますので、

その申し込みと、開始までにより一層のお勉強と、

・・というのを片手間にやりつつの、本業と。

忙しい日々が続きますが、頑張ります。

2010年6月26日土曜日

ITコーディネータ 筆記試験合格

第18回ITコーデイネータの筆記試験を5月23日に受験していました。

6月25日にWEBサイトで合格者発表を速報的に掲載されてました。

それが、どうやら、合格できたみたいです。(多分)



ご存知ない方のために付記しますと、

ITコーディネータというのは、経営者とITベンダーとの橋渡しを

するための役割を担うための民間資格です。

そのため、次の能力・技能・知識を要求されます。

   (1)経営と情報技術の双方の知識

   (2)経営者層をはじめ各ステイクホルダーとの
     コミュニケーション能力

   (3)情報システム構築プロジェクトの監理能力

そして、ITコーディネータの資格を取得するには、

筆記試験合格とケース研修修了の両方を

クリアしなくてはなりません。



私は、筆記試験には無事合格しましたので、

今度は、ケース研修の修了を目指すことになります。

受講料525,000円で、期間は8/20~11/20のようです。

※最初にこの受講料を見たとき、
 私も「えっ?!」と驚きました。



7月1日に合格証が発送されるということで、

いずれにしてもそれを受け取ってからの話になります。



間違いなく合格していたら、

ケース研修に申し込み、11月までみっちりと頑張る所存です。

2010年6月24日木曜日

Xoops(Cube)でtabパネル


あまり重要でないけど表示したいといったコンテンツをタブでまとめたり、
派生項目がたくさんあるようなコンテンツをタブで分散したり、
というようなことは、アプリケーションではよくありますよね。

今回、jQueryを使ってXoops(Cube)でタブを実現する方法について、
少し試してみましたので、以下で説明します。



■0.今回の前提

(1)「jQueryを使ったタブ」のサンプルとして、こちらのサンプルを参照します。
(2)Xoops画面に配置した幾つかのブロックをタブにまとめることを目標とする


■1.そもそもjavascriptって、どうやって取り込むの?

jQueryは、要するに、javascriptです。

フツーのHTMLの場合、javascriptを取り込むには、
<head>~</head>で囲まれた部分に、
   <script type="text/javascript">
    alert("こんにちは!");
   </script>
のように書いたり、
   <script type="text/javascript" src="hogehoge.js"></script>
のように書いたりします。
※<body>~</body>で囲まれた部分に書くことも出来ますが、
 可読性や保守性を考慮して、出来るだけ避けたほうが良いようです。

Xoops(Cube)でjqueryのようなJavascriptを取り込む場合も同じです。

ただ、Xoops(Cube)の場合、~.htmlがアチコチにあるようなイメージがあります。
どのhtmlをイジればよいのか判断するためには、予備知識が必要になりますので、
次節で説明します。



■2.そもそも何をイジればよいのか ※Xoops(Cube)の画面について

「幾つかのブロックをタブにまとめる」のようなことをする場合、
そもそも、どのファイルをイジればよいのか、分かるでしょうか?
それを理解するためには、Xoops(Cube)の画面描画について
理解することが必要です。

奥深く見ていけばキリがありませんが、極めてザックリとでも
理解しておくことで、「どこを触ればいいのか」が分かるようになります。

Xoops(Cube)の画面描画は、次のような流れになっています。
    ・各モジュール・ブロックのtemplate
              ↓
    ・PHP(Smarty)で解析
              ↓
    ・PHP変数($xoops_lblockとか)に格納
              ↓
    ・テーマのtemplateにPHP変数をセット
              ↓
    ・テーマのtemplateをPHP(Smarty)で解析
              ↓
    ・Xoops(Cube)の画面として描画

つまり、テーマという大枠のhtmlの中に、
各モジュールやブロックが自動的に組み込まれていきます。
※自動的に組み込まれていく流れが、上記の流れになります。

そもそも、htmlファイルの中に<head>~</head>が現れる部分は
テーマにしかありません。


従って、今回イジるのは、テーマのhtmlファイルということになるワケです。



■3.そもそもタブって、どうやってるの?

これは、Xoopsの話ではなく、htmlとかjavascriptの話になります。
※一般的にタブでやってることをXoops(Cube)にも適用するだけの話、
 ということです。

ここで、最初のほうで挙げたサンプルを参照してみます。

まず、タブに表示するデータはすべて、<ul>でHTMLに書いてあります。
<div id="container">
    <ul class="tab">
        <li><ahref="#tab1"class="selected">JavaScript</a></li>
        <li><ahref="#tab2">CSS</a></li>
        <li><ahref="#tab3">HTML</a></li>
        <li><ahref="#tab4">jQuery</a></li>
        <li><ahref="#tab5">XHTML</a></li>
    </ul>
    <ul class="panel">
        <li id="tab1">aaa<li>
        <li id="tab2">bbb<li>
        <li id="tab3">ccc<li>
        <li id="tab4">ddd<li>
        <li id="tab5">eee<li>
    </ul>
</div>
タブのラベルに表示する文字はclass="tab"のulに、
タブの内容として表示するデータはclass="panel"のulに
それぞれ書かれているのが分かります。

このHTMLに対して、javascript(jQuery)で
panelでは現在選択されているタブの内容だけ表示する
ように制御しています。
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script> //・・・(A)
<script type="text/javascript">
$(function(){ //・・・(B)
    $("ul.panel li:not("+$("ul.tab li a.selected").attr("href")+")").hide();
    $("ul.tab li a").click(function(){
        $("ul.tab li a").removeClass("selected");
        $(this).addClass("selected");
        $("ul.panel li").slideUp("fast");
        $($(this).attr("href")).slideDown("fast");
        return false;
    });
});
</script>
上記(A)の部分では、Google API経由でjQueryを参照しています。
また、上記(B)の関数内で、
panelでは現在選択されているタブの内容だけ表示する
ように制御しています。

ただし、少し手抜きが過ぎるようです。
つまり、panelに表示するデータとして、ulタグによるリストを表示しようとすると
該当リストが表示されなくなります。

そのため、次のように改造してやります。
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script> //・・・(A)
<script type="text/javascript">
$(function(){ //・・・(B)
    $("ul.panel > li:not("+$("ul.tab li a.selected").attr("href")+")").hide(); //・・・(modify 1)
    $("ul.tab li a").click(function(){
        $("ul.tab li a").removeClass("selected");
        $(this).addClass("selected");
        $("ul.panel > li").slideUp("fast"); //・・・(modify 2)
        $($(this).attr("href")).slideDown("fast");
        return false;
    });
});
</script>
分かりにくいですが、panelのliを参照する箇所を
「panel li」ではなく「panel > li」
に変更してあります。

あとは、CSSを使って、class="tab"の部分をタブらしくヨコに並べるなどします。
※display:blockにしてfloat:leftするなど、お馴染みの方法です。
 この例だと、class="panel"側でclear:bothしているようです。
 余談ですが、こういうのもCSS3だとラクが出来るそうでして、
 いわゆるclear-fix的なテクニックが不要になるらしいです。



■4.Xoops(Cube)でタブを使ってみよう

すっかり前提が長くなってしまいましたが、
ここまで、上記1.2.3.と説明してきた内容を元に
いよいよXoops(Cube)でタブを使ってみましょう。

大まかな手順は、上記3.から、
    ・「HTMLの編集」
        ↓
    ・「javascript(jQuery)の導入」
        ↓
    ・「cssで見た目を整える」

とすればよいことが分かります。

さらに、上記2.から、
「HTMLの編集」は、「テーマ(HTML)の編集」をすればよい
ことが分かりますので、Xoops(Cube)の場合、
    ・「テーマ(HTML)の編集」
        ↓
    ・「javascript(jQuery)の導入」
        ↓
    ・「cssで見た目を整える」

といった手順で、Xoops(Cube)にタブを導入してみます。



手順1:「テーマ(HTML)の編集」

今回は、LEFTブロックに配置されるブロックをすべてタブに入れてみます。
テーマ(HTML)のLEFTブロックに該当する部分について、
上記3.と同じようにするために、次のように修正します。
<div id="container">
    <ul class="tab">
<{foreach item=block from=$xoops_lblocks name=tab_title}>
        <li><a href="#tab<{$smarty.foreach.tab_title.iteration}>" <{if $smarty.foreach.tab_title.first}>class="selected"<{/if}>><{$block.title}></a></li>

<{/foreach}>
    </ul>
    <ul class="panel">
<{foreach item=block from=$xoops_lblocks name=tab_content}>
        <li id="tab<{$smarty.foreach.tab_content.iteration}>"><{$block.content}></li>
<{/foreach}>
    </ul>
</div>




手順2:「javascript(jQuery)の導入」

上記1.で説明したように、<head>~</head>で囲まれた箇所に
javascript(jQuery)を導入するための記述を追加します。
追加する内容は、上記3.で説明したjavascriptをそのまま転記すれば大丈夫です。
転記する場所は、テーマ(HTML)の<head>~</head>で囲まれた箇所の
最後にでも書いておけば大丈夫です。
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
    $("ul.panel > li:not("+$("ul.tab li a.selected").attr("href")+")").hide();
    $("ul.tab li a").click(function(){
        $("ul.tab li a").removeClass("selected");
        $(this).addClass("selected");
        $("ul.panel > li").slideUp("fast");
        $($(this).attr("href")).slideDown("fast");
        return false;
    });
});
</script>




手順3:「cssで見た目を整える」

上記3.でも触れたように、タブをタブらしく見せるなど、
cssで見た目を整えてあげましょう。

リストをヨコに並べるには、「float:left;」を使うのが定番みたいです。
「float:left;」だとそれを解除するコードが必要になりますが、
この辺りのテクニックは「clear-fix」で検索すればたくさん見つかります。

タブの背景画像をオンラインで作ってくれるサービスなんかも
検索すればたくさん見つかります。

デザインを頑張るのは、割と楽しいです。(仕事でなければ・・・)
どうか頑張ってデザインしてみてください。




・・・長くなりましたが、以上で説明は終わりですが
どうだったでしょうか?

結局、長々と説明した割りには、Xoops(Cube)に特化した部分は
ほとんど出てきませんでした。

実は、Xoops(Cube)だからって、何か特別なことをしなければ
いけないということはありません。

でも、Xoops(Cube)って難しいと思われがちで、敬遠されがちなので、
こういったエントリを書いてみました。


皆様の何かのご参考になれば、幸いです。

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");


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