PowerCMS X ブログ
2024-12-14
PAMLクラスは 1ファイル、非常に軽量で高速に動作するテンプレートエンジンです。PowerCMS Xのビュー(テンプレート)をビルドするものという理解をされているかと思うのですが、実はヘッドレスCMSから JSONを取得して MTMLで組み立てるようなこともできなくはありません。
どのようにMTMLをパースして、PHPコードに変換しているかについて、処理がよく理解できない、という社内の声がありまして、いつか纏めなきゃね、ということで本日の記事となります。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title><mt:var name="title" escape></title>
</head>
<body>
<main>
<h1><mt:var name="title" escape></h1>
<h2>テンプレートのビルドテスト</h2>
<p id="headline"><mt:var name="description" escape></p>
<mt:loop name="list_items">
<mt:if name="__first__"><ul></mt:if>
<li><mt:var name="__value__"></li>
<mt:if name="__last__"></ul></mt:if>
</mt:loop>
</main>
</body>
</html>
このようなテンプレートがあった時、以下のコードで処理した結果を出力できます。
$mtml = file_get_contents( 'template.tmpl' );
$ctx = new PAML();
$ctx->init();
$ctx->prefix = 'mt';
$ctx->vars['title'] = 'タイトル';
$ctx->vars['description'] = '記事の概要です。';
$ctx->vars['list_items'] = [1,2,3];
echo $ctx->build( $mtml );
この時、内部的には compileメソッドを通っていて、このメソッドがテンプレートエンジンの心臓部となります。
compile( string $content, bool $disp = true, array|null $tags_arr = null, array $use_tags = [],
array $params = [], bool $compiled = false, bool $nocache = false, string|null $orig_key = null ) : string
第6引数の $compiled に trueを渡すと、コンパイル済みの PHPコードを返します。
$compiled = $ctx->compile( $html, true, null, [], [], true );
echo '<pre>';
echo htmlspecialchars( $compiled );
処理の途中のテンプレートの状態とともに、やっていることを簡単に解説します。
%_2795dd!DOCTYPE html_2795dd%
%_2795ddhtml lang="ja"_2795dd%
%_2795ddhead_2795dd%
%_2795ddmeta charset="utf-8"_2795dd%
%_2795ddtitle_2795dd%{%mtvar name="title" escape /%}%_2795dd/title_2795dd%
%_2795dd/head_2795dd%
%_2795ddbody_2795dd%
%_2795ddmain_2795dd%
%_2795ddh1_2795dd%{%mtvar name="title" escape /%}%_2795dd/h1_2795dd%
%_2795ddh2_2795dd%テンプレートのビルドテスト%_2795dd/h2_2795dd%
%_2795ddp id="headline"_2795dd%{%mtvar name="description" escape /%}%_2795dd/p_2795dd%
{%mtloop name="list_items"%}
{%mtif name="__first__"%}%_2795ddul_2795dd%{%/mtif%}
%_2795ddli_2795dd%{%mtvar name="__value__" /%}%_2795dd/li_2795dd%
{%mtif name="__last__"%}%_2795dd/ul_2795dd%{%/mtif%}
{%/mtloop%}
%_2795dd/main_2795dd%
%_2795dd/body_2795dd%
%_2795dd/html_2795dd%
<dom__73c019>%_2795dd!DOCTYPE html_2795dd%
%_2795ddhtml lang="ja"_2795dd%
%_2795ddhead_2795dd%
%_2795ddmeta charset="utf-8"_2795dd%
%_2795ddtitle_2795dd%__dom__73c019__<mtvar name="title" escape />__dom__73c019__%_2795dd/title_2795dd%
%_2795dd/head_2795dd%
%_2795ddbody_2795dd%
%_2795ddmain_2795dd%
%_2795ddh1_2795dd%__dom__73c019__<mtvar name="title" escape />__dom__73c019__%_2795dd/h1_2795dd%
%_2795ddh2_2795dd%テンプレートのビルドテスト%_2795dd/h2_2795dd%
%_2795ddp id="headline"_2795dd%__dom__73c019__<mtvar name="description" escape />__dom__73c019__%_2795dd/p_2795dd%
__dom__73c019__<mtloop name="list_items">__dom__73c019__
__dom__73c019__<mtif name="__first__">__dom__73c019__%_2795ddul_2795dd%__dom__73c019__</mtif>__dom__73c019__
%_2795ddli_2795dd%__dom__73c019__<mtvar name="__value__" />__dom__73c019__%_2795dd/li_2795dd%
__dom__73c019__<mtif name="__last__">__dom__73c019__%_2795dd/ul_2795dd%__dom__73c019__</mtif>__dom__73c019__
__dom__73c019__</mtloop>__dom__73c019__
%_2795dd/main_2795dd%
%_2795dd/body_2795dd%
%_2795dd/html_2795dd%</dom__73c019>
<?php $_vars=&$this->vars;$_old_params=&$this->old_params;$_local_params=&$this->local_params;$_old_vars=&$this->old_vars;$_local_vars=&$this->local_vars;?><!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title><?php echo paml_htmlspecialchars($this->function_var($this->setup_args(['name'=>'title','escape'=>'','this_tag'=>'var'],null,$this),$this),ENT_QUOTES)?>
</title>
</head>
<body>
<main>
<h1><?php echo paml_htmlspecialchars($this->function_var($this->setup_args(['name'=>'title','escape'=>'','this_tag'=>'var'],null,$this),$this),ENT_QUOTES)?>
</h1>
<h2>テンプレートのビルドテスト</h2>
<p id="headline"><?php echo paml_htmlspecialchars($this->function_var($this->setup_args(['name'=>'description','escape'=>'','this_tag'=>'var'],null,$this),$this),ENT_QUOTES)?>
</p>
<?php $c_6f6ea6=null;$_old_params['_6f6ea6']=$_local_params;$_old_vars['_6f6ea6']=$_local_vars;$a_6f6ea6=$this->setup_args(['name'=>'list_items','this_tag'=>'loop'],null,$this);$_6f6ea6=-1;$r_6f6ea6=true;while($r_6f6ea6===true):$r_6f6ea6=($_6f6ea6!==-1)?false:true;echo $this->block_loop($a_6f6ea6,$c_6f6ea6,$this,$r_6f6ea6,++$_6f6ea6,'_6f6ea6');ob_start();?>
<?php $c_6f6ea6 = true; if(isset($this->local_vars['__total__'])&&isset($this->local_vars['__counter__'])&&$this->local_vars['__total__']<$this->local_vars['__counter__']){$c_6f6ea6=false;}if($c_6f6ea6 ):?>
<?php $_old_params['_fe4142']=$_local_params;$_old_vars['_fe4142']=$_local_vars;if($this->conditional_if($this->setup_args(['name'=>'__first__','this_tag'=>'if'],null,$this),null,$this,true,true)):?>
<ul><?php endif;$_local_params=$_old_params['_fe4142'];$_local_vars=$_old_vars['_fe4142'];?>
<li><?php echo $this->function_var($this->setup_args(['name'=>'__value__','this_tag'=>'var'],null,$this),$this)?>
</li>
<?php $_old_params['_544a4d']=$_local_params;$_old_vars['_544a4d']=$_local_vars;if($this->conditional_if($this->setup_args(['name'=>'__last__','this_tag'=>'if'],null,$this),null,$this,true,true)):?>
</ul><?php endif;$_local_params=$_old_params['_544a4d'];$_local_vars=$_old_vars['_544a4d'];?>
<?php endif;$c_6f6ea6=ob_get_clean();endwhile; $_local_params=$_old_params['_6f6ea6'];$_local_vars=$_old_vars['_6f6ea6'];?>
</main>
</body>
</html>
結果は compile_dir 配下に md5(テンプレートのソース).php ファイル名で保存して、それがあれば次回はそれを使います。
PowerCMS Xのビューや URLマップは、保存時に compiledカラムに結果を保存し、それを使います。
いかがでしたでしょうか。せっかくなので、プラグインなどから利用できるPAMLのいくつかのメソッドについても紹介します。
PAML : PHP Alternative Markup Language
予約変数「__first__」「__last__」「__even__」「__odd__」「__index__」「__counter__」「__end__」「__total__」をローカル変数にセットする
$ctx->set_loop_vars( $counter, $params );
ローカル変数、グローバル変数いずれかににマッチする値を返す $var には「local_vars」「vars」が指定可能、省略時はローカル変数、グローバル変数の順に検索する
$ctx->get_any( $name );
プロパティ「template_paths(ディレクトリのパスの配列)」から該当の(優先)テンプレートファイルのパスを返す
$ctx->get_template_path( 'edit.tmpl );
append, prepend, functionタグ属性に沿って処理を行う
$ctx->append_prepend( $content, $args, $name, $var );
変数をブロック内に局所化する(local_varsについては局所化の必要はありません)
$ctx->localize( ['var1', 'var2'...] );
局所化した変数を元に戻す
$ctx->restore( ['var1', 'var2'...] );
条件タグで eq などの比較が可能になる
$ctx->conditional_if( $args, $content, $ctx, $repeat, $context );
PAML::math_operation( $args['op'], $value, $content, $args );
opモディファイアの処理を行う
ブロックタグで第4引数 $repeat に値をセットすることでループを終了する
$repeat = $ctx->false();
function hdlr_blocktag_name ( $args, $content, $ctx, &$repeat, $counter ) {
// $app = $ctx->app;
$local_vars = ['local_var1', 'local_var2', 'local_var3'];
if (! $counter ) {
$loop_params = ['var1', 'var2', 'var3'];
if ( empty( $loop_params ) ) {
$repeat = $ctx->false(); // ループをスキップする時
return;
}
$ctx->localize( $local_vars ); // 変数のローカライズ
$ctx->local_params = $loop_params;
}
$params = $ctx->local_params;
$ctx->set_loop_vars( $counter, $params ); // 予約変数をセット
if ( isset( $params[ $counter ] ) ) {
$param = $params[ $counter ];
$ctx->local_vars['__value__'] = $param; // ローカル変数 __value__ に値をセット
$repeat = true;
} else {
$ctx->restore( $local_vars ); // 変数のリストア
$repeat = $ctx->false(); // ループを終了
}
return $content;
}
カテゴリー:技術情報
投稿者:Junnama Noda