それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

includeとextractの組み合わせでテンプレート処理を作る。PHPのAdvent Calender #9

PHPのAdvent Calender #9 担当のtakuya_1stです。

#8はこちら→子プロセス制御ふたたび : PHP Advent Calendar jp 2011 Day 8 - Blog::koyhoge

私のテーマはテンプレート処理です。

PHPでPHPテンプレートを実行してみよう。


PHPPHPのコードを実行するだと、

  • system
  • exec
  • eval

が思いつきそうですが、今回はinclude です。

つまり、今日のテーマは「include を見つめ直して惚れ直してみる。」です。include にextractとob_start を組み合わせます。これを作ってテンプレートを作れるようになりましょう。Smartyに頼らなくてもデザイナに優しいテンプレートを作れます。

includeと組み合わせて使うと便利な次のテクニックでPHPからPHPをテンプレートとして実行します。

  • ob_start
  • extract
  • 可変インクルード

include とは

include は外部のPHPファイルを実行して、ファイルを取り込む関数です。include関数を使うテンプレート処理を作ってみます。index.phpとtemplate.phpを作ります。それぞれが次のようなファイルです。

template.php
<?php 
echo “こんにちは $USER さん;
index.php
<?php
$USER = “takuya”;
include ‘template.php’;
実行結果

ただ、このままでは、$USER 変数が共通変数なので、ほかの変更が影響してしまう。

includeを メソッドにして、変数を守ってみる。

 <?php
 $USER = "harushige";
 function display(){
   $USER = "takuya";

   include "template.php";
 }
 
 display();


こうすると$USER 変数はメソッドの中だけで有効なので、ほかに影響しません。


extract で好きな変数を入れてみる。

変数を局所スコープで守ることが出来ました。しかし引数の数が多くなると不便です。

面倒:表示用の変数が多くなると

引数指定はしたくない。

function display($USER,$KANA,,,,,,){
//
}

引数にテンプレート変数を指定すると、増えすぎて困る。じゃぁArrayで渡す?

テンプレート側もシンプルさが失われる

配列で渡した場合。

<?php
echo$params[\"KANA\"] さんのログイン名は $params[‘USER’] “;

テンプレート側で配列指定が必要。 " / ' のエスケープ処理やらで優しくない。読みにくくなる。

そこで extract 関数を使う。

たくさんの変数を渡すのは面倒なので、一括して渡したい。そのためのextract。

<?php
$USER = "harushige";

function display($params){
  extract($params);
  include "template.php";
}
$params = array();
$params["USER"] = "takuya";
$params["KANA"] = "たくや";
display($params);

extract 関数は、配列を変数に展開してくれる便利な関数です。

実行結果

テンプレート側

<?php
echo "こんにちは $USER さん\n";
echo " $KANA さんのログイン名は $USER です。\n";

phpのテンプレートはシンプルなままですね。


$this->username $params[“username”] を使わずとも、テンプレート側で、$username が使えるようになる。extract 便利。

extract は何をしているのか。

実質、次と同じです。

<?php
foreach( $params as $key => $val ){
     eval($key = $val);
}

extract があれば、変数名を$thisから参照しなくてもいいので、HTMLデザイナさんにも優しくなります。

テンプレート側の出力結果を文字列に保存する。

出力を毎回Echoするのは面倒です。かといって文字列にするのもうれしくない。

やりたくない例

<?php
$html = "こんにちは $USERさん";
$html .=<div>$params[‘KANA’] さんのログイン名は $params[‘USER’] </div>“;

これはやりたくない。

出力結果取り出す。ob_start を活用する。

render($params){
  extract($params);
  ob_start();

  include "template.php";
  $html = ob_get_contents();
  ob_end_clean();

  return $html;
}

$params = array();
$params["USER"] = "takuya";
$params["KANA"] = "たくや";

$html = render($params);
echo $html;

これで、既存のテンプレートの出力を取得できて、使い回せる。うれしい。

動的にテンプレートのファイルを変える。

テンプレートファイルの名前が固定なのがうれしくないです。テンプレート名は動的に変えた方が便利でしょう。

<?php
function render($params){
  extract($params);
  ob_start();
  include$TEMPLATE_NAME.php";
  $html = ob_get_contents();
  ob_end_clean();
  return $html;
}

$params = array();
$params["USER"] = "takuya";
$params["KANA"] = "たくや”;
$params[“TEMPLATE_NAME”]  = “template”;
$html = render($params);

echo $html;

これで、読み込むテンプレートファイルを変えることが出来ます。

動的にinclude ファイルを作って、実行して捨てる。

これが最後になります。

文字列をテンプレートとして実行したい。
PHPのコードを動的に作ってIncludeしたい。

eval でも良いのですが、include を使う方針でやってみます。変数もちまわすことなくなり、うれしいです。

<?php
function render($params){
  extract($params);
  ob_start();
  include "$TEMPLATE_NAME";
  $html = ob_get_contents();
  ob_end_clean();
  return $html;
}

function render_string($php_str,$params){
  $tmpname = tempnam("/tmp/", "test");
  file_put_contents($tmpname, $php_str);
  $params["TEMPLATE_NAME"] = $tmpname;
  $html =  render($params);
  unlink($tmpname);
}

$params = array();
$params["USER"] = "takuya";
$params["KANA"] = "たくや";
//ここから
$str = '<?php echo "文字列からこんにちは $KANA($USER) さん。\n";  ';
$html = render_string($str, $params);
echo $html;
実行結果


ポイント:一時ファイルの作り方。

tmpnam 関数で 一時ファイル名を作ることが出来ます。

PHP文字列コードをテンプレートファイルに保存テンプレートとして実行。こうすることで、テンプレートを作った先でさらに実行など便利なことが出来ます。

しかしこのようなコードは実行できない

もともと、次を試みたのですが、PHPでは出来ないようです。

<?php
file_put_contents( "php://temp",$php_code_str );
include "php:/temp"

これが出来れば。。これ理想なんですけどね。現在のPHPの仕様では出来ない感じです。残念。

JavaRubyにはStringIOというファイルのフリする文字列クラスが用意されているのですが。。。このへんがPHPの限界のようです。
PHPの限界に到達したところで、今回の記事は終了です。

まとめ

  • extract でテンプレート用変数の配列は、変数にする。
  • ob_start でEchoした文字列をバッファリング。
  • include は動的に変えられる

extract / include / ob_start の組み合わせは楽ですね。

PHPは「テンプレート言語」。テンプレートライブラリに頼らずともちゃんと処理できるんですよ。include を見直して惚れ直してみましょう。

明日は#10

明日は、@calpoさんです。