アルファサード株式会社 代表取締役 野田 純生のブログ


PHPerのための Movable Type 講座(その5)


公開日 : 2014-04-16 13:01:55


暖かくなりましたね。続きます。

class MTViewer( extends Smarty) 編。$ctxです。そう、コンテキストはSmartyであるのですが、今日はこれ。テンプレートエンジンの謎を探る(謎というわけでもないが)。PHPでのMTタグの作り方です。

公式ドキュメントは以下。

公式ドキュメントではPerl(スタティック)の解説が前半、後半がPHPになっていますが、基本的にはタグの作成方法についてのドキュメントではPHPについても言及されています。

ベースにするのは昨日の以下のコード。当面「生」PHPで行くことにします。そろそろプラグインに行きたいところであるけれども。

この例では、「生」PHPですが、MTのお作法に従ってプラグインとして作る場合も基本は一緒です。$ctxのメソッドで動的にタグが登録しているという違いがあるだけで、基本は同じです。


    <?php
        $blog_id = 1;
        include('/path/to/mt/php/mt.php');
        $mt = MT::get_instance( $blog_id, '/path/to/mt/mt-config.cgi' );
        $ctx =& $mt->context();
        $ctx->caching = FALSE;
        $blog = $mt->db()->fetch_blog( $blog_id );
        $ctx->stash( 'blog', $blog );
        $ctx->stash( 'blog_id', $blog->id );
        $ctx->__stash[ 'vars' ][ 'foo' ] = 'bar';
        $tmpl = <<< EOM
        <pre>
        <mt:var name="foo">
        <mt:BlogURL setvar="blog_url">
        <MTBlogName>
        <MTEntries include_blogs="children">
        <MTEntryTitle>
        </MTEntries>
EOM;
        require_once( 'MTUtil.php' );
        require_once( 'modifier.mteval.php' );
        $contents = smarty_modifier_mteval( $tmpl, TRUE );
        echo $contents;
        echo $ctx->__stash[ 'vars' ][ 'blog_url' ];
    ?>

動的にテンプレートタグを追加する

add_tag

タグ名と、関数名を渡して、ファンクションタグを登録します。以下の例では、mt:Bar タグで、Barを出力します。

$ctx->add_tag( 'bar', 'bar' );

function bar( $args, &$ctx ) {
    return 'Bar';
}

// <mt:bar> => Bar

add_container_tag

タグ名と、関数名を渡して、ブロックタグを登録します。以下の例では、<mt:buz>〜</mt: buz>で、1から5までの数字を<mt:var name="__counter__">タグで出力します。

$ctx->add_container_tag( 'buz', 'buz' );

function buz( $args, $content, &$ctx, &$repeat ) {
    $localvars = array( '__counter__' );
    if ( isset( $args[ 'max' ] ) ) {
        $max = $args[ 'max' ];
    } else {
        $max = 5;
    }
    if (! isset( $content  ) ) {
        $ctx->localize( $localvars );
        $ctx->__stash[ 'vars' ][ '__counter__' ] = 0;
    }
    $counter = $ctx->__stash[ 'vars' ][ '__counter__' ];
    if ( $counter < $max ) {
        $repeat = TRUE;
        $counter++;
        $ctx->__stash[ 'vars' ][ '__counter__' ] = $counter;
        $ctx->stash( 'counter', $counter );
    } else {
        $ctx->restore( $localvars );
        $repeat = FALSE;
    }
    return $content;
}

ブロックタグの作り方は、ファンクションタグと違って少し複雑ですが、以下のように理解してください。

  1. 引数は、$args, $content, &$ctx, &$repeat
  2. $argsには、タグに渡されるモディファイアがキーバリューの配列で格納されている
  3. $repeat が TRUEの間、繰り返し呼び出される
  4. ループの1回目は、isset( $content ) はFALSEである(なので、これで分岐して初期化等を行う)
  5. $ctx->localize( $localvars ); でテンプレート変数の初期化
  6. $ctx->restore( $localvars ); で、初期化したテンプレート変数をリストア(戻す)

5、6ですが、ループの中でループを使っている時なんかで、__counter__の値が親ループの中の値なのか、子ループの中の値なのかが同じ変数名なのでわかんなくなりますよね。変数のスコープを局所化するという理解で良いと思います。

__stash は、クラスのプロパティ、stashはクラスのメソッドです。ややこしい? けど、要はキャッシュです。値をセットするのに使われます。stashメソッドでは第一引数にキャッシュのキー、第二引数にキャッシュの値(オブジェクトや配列、もしくはスカラ何でも)を指定します。第二引数を省略すると、キャッシュされた値を取得します。

$ctx->stash( 'blog', $blog );

__stashはプロパティで、配列(のキーが) varsに指定した値が、mt:var name='"hoge"で取得できます。

$ctx->__stash[ 'vars' ][ '__counter__' ] = $counter;

add_conditional_tag

タグ名と、関数名を渡して、条件タグ(mt:ifなんとか)を登録します。以下の例では、常に真を返す mt:ifqux条件タグを登録しています(常に真、って意味がないんですけど、サンプルとして)。

$ctx->add_conditional_tag( 'ifqux', 'ifqux' );

function ifqux( $args, $content, &$ctx, &$repeat ) {
    return $ctx->_hdlr_if( $args, $content, $ctx, $repeat, TRUE );
}

返り値 $ctx->_hdlr_if( $args, $content, $ctx, $repeat, TRUE ); のように、第5引数に真偽を指定します。

add_global_filter

モディファイア名と、関数名を渡して、グローバルモディファイアを登録します。以下の例ではモディファイアに渡した文字列を連結して出力するcorgeモディファイアを登録しています。

$ctx->add_global_filter( 'corge', 'corge' );

function corge( $text, $arg ) {
    return $text . $arg;
}

// <mt:bar corge="!!"> => Bar!!

ちょっと長くなりますが、ソースは下記のようになります。

    <?php
        $blog_id = 1;
        include('/path/to/mt/php/mt.php');
        $mt = MT::get_instance( $blog_id, '/path/to/mt/mt-config.cgi' );
        $ctx =& $mt->context();
        $ctx->caching = FALSE;
        $blog = $mt->db()->fetch_blog( $blog_id );
        $ctx->stash( 'blog', $blog );
        $ctx->stash( 'blog_id', $blog->id );
        $ctx->add_tag( 'bar', 'bar' );
        $ctx->add_container_tag( 'buz', 'buz' );
        $ctx->add_conditional_tag( 'ifqux', 'ifqux' );
        $ctx->add_global_filter( 'corge', 'corge' );
        $tmpl = <<< EOM
        <pre>
        <mt:bar corge="!!">
        <mt:buz>
        <mt:var name="__counter__">
        </mt:buz>
        <mt:ifqux>
        It is true.
        <mt:else>
        It is false.
        </mt:ifqux>
EOM;
        require_once( 'MTUtil.php' );
        require_once( 'modifier.mteval.php' );
        $contents = smarty_modifier_mteval( $tmpl, TRUE );
        echo $contents;
        
        function bar( $args, &$ctx ) {
            return 'Bar';
        }
        
        function buz( $args, $content, &$ctx, &$repeat ) {
            $localvars = array( '__counter__' );
            if ( isset( $args[ 'max' ] ) ) {
                $max = $args[ 'max' ];
            } else {
                $max = 5;
            }
            if (! isset( $content  ) ) {
                $ctx->localize( $localvars );
                $ctx->__stash[ 'vars' ][ '__counter__' ] = 0;
            }
            $counter = $ctx->__stash[ 'vars' ][ '__counter__' ];
            if ( $counter < $max ) {
                $repeat = TRUE;
                $counter++;
                $ctx->__stash[ 'vars' ][ '__counter__' ] = $counter;
                $ctx->stash( 'counter', $counter );
            } else {
                $ctx->restore( $localvars );
                $repeat = FALSE;
            }
            return $content;
        }
        
        function ifqux( $args, $content, &$ctx, &$repeat ) {
            return $ctx->_hdlr_if( $args, $content, $ctx, $repeat, TRUE );
        }
        
        function corge( $text, $arg ) {
            return $text . $arg;
        }
    ?>

出力結果は、以下のようになります。

Bar!!

1

2

3

4

5


It is true.

他に、add_token_tag というのがあるのですが(mt:subcategoriesのように再帰的に呼ばれるタグ)、使うケースは少ないように思いますので、今回は省略しました。

では、また。



このブログを書いている人
野田純生の写真
野田 純生 (のだ すみお)

大阪府出身。ウェブアクセシビリティエバンジェリスト。 アルファサード株式会社の代表取締役社長であり、現役のプログラマ。経営理念は「テクノロジーによって顧客とパートナーに寄り添い、ウェブを良くする」。 プロフィール詳細へ