PHPでテンプレートエンジンを作る2

投稿日:2019年04月15日 08時34分04秒

前回のPHPテンプレートエンジンを1行で自作するで、わりとうまく出来たので、テンプレートエンジンを使って実装を開始してます。includeを使ったテンプレートエンジンは散見されるので、うまく行ったテクニック等をまとめておきます。
参考includeとextractの組み合わせでテンプレート処理を作る。

1.includeでテンプレートエンジンを作る
テンプレートエンジンに求められるのはテンプレート側からの副作用で呼び出す側の変数が壊されない事です。よってこれを関数の形で呼び出します。呼び出すにはクラスを使ったり関数定義したりしますが、前回の変数に関数を割り当てる方法が一番楽でしょう。

main.php


$display = function($name){include('sample.php');};
$name = '蓬莱';
display($name);
echo $name;

テンプレート:sample.php

echo $name;
$name = "";

ここでテンプレート側は拡張子をphpにするといいと思います。.htmlにすると直接呼び出した時に変数が見えてしまいます。拡張子をphpにすることで、変数が未定義なためにエラーになります。includeされないと表示されないというのは利点です。

2.extractを使う
詳細は参考を見てください。これは便利ですね。
呼び出し側

//関数定義
$disp = function($data,$target){
    extract($data);
    include("template/$target");
};
//呼び出し
$disp(array('name'=>'pochi','city'=>'sapporo'),'address.php');

テンプレート側

    名前:<?php echo $name?><br>
    住所:<?php echo $city?><br>

当然テンプレートは拡張子phpでも先頭に extractは連想配列のキーを値とした変数を生成します。

3.出力結果を文字として取得する
標準出力を切り替えればいいようです。上の例では、ob_start()を使って以下の様に書けます。

//関数定義
$disp = function($data,$target){ extract($data); include("template/$target"); };
//呼び出し
ob_start();
$disp(array('name'=>;'pochi','city'=>;'sapporo'),'address.php');
$html = ob_get_contents();
ob_end_clean();
echo "$html";

4.一時ファイルをincludeする
小さいファイルなのでメモリを使いたい。これを実現しようと結構悩みましたがphp://memoryやphp://tempはストリームなので結局無理っぽいです。よって一時ファイルを使います。tempnam()等は最後に消さないといけないので、tmpfile()が便利です。ストリームのクローズでファイルが消えるようです。ここでは明示的にクローズして消していますので関数の外側には影響がありません。

$tempinc = function($param,$data) {
  $file = stream_get_meta_data($fp = tmpfile());
  file_put_contents( $file['uri'],$data );
  extract($param);
  include( $file['uri']);
  fclose($fp);
};
$tempinc(array('name'=>;'pochi'),'name=><?php echo $name;?>');

このようにtmpfile()からファイルポインタを取得し、stream_get_meta_dataでファイル名を取得、includeするという流れが一番良さそうです。途中でファイルポインタも取っていますからflose($fp)すれば一時ファイルは消えます。本当はこれを全てメモリ上でやりたいのですが、tmpfile()をfopen(‘php://memory’,’w’)として取得したファイル情報ではファイル名がきちんと設定されていません。よってincludeは難しいようです。

5.テンプレート側からformを使う
$_GETや$_POSTはグローバル宣言しなくても使えます。よって特に宣言しなくてもincludeするだけでformの値を受け渡せます。
下記の例は単純にフォームをテンプレートにしてますが、この例自体をincludeしても動作します。受けた値によってformを変更したりするのも簡単です。

$mode = isset($_POST['mode'])?$_POST['mode']:null;
$form = function($param){
 extract($param);
 include('template.php');
};
$form(array('mode'=>$mode*2));

フォームテンプレート

<form method="POST">
  <input name="mode" type="text" value="<?php echo $mode;?>" />
  <input type="submit" />
</form>

自力でテンプレートエンジンを書くと、どこでも100%PHPのコードが書けて素晴らしい状況です。是非includeのテンプレートを使いましょう!

[<< PHPテンプレートエンジンを1行で自作する]

[phpのforeachで一つ前の要素を変更する >>]