Phelocity (Velocity風PHPテンプレートエンジン)

今まで、PHPでそれ程沢山のページがあるシステムを作ったことが無かったので、PHPのテンプレートエンジンは、不要だと思ってた。PHP自体がテンプレートエンジンなんだから、別の文法とか導入するのは、あまり意味のあることだとは思えなかった。


でも、一人で結構ページ数の多いシステムをPHPで作ってると、さすがに面倒になってくる。何が面倒って、「<?php」のタグを閉じるのが面倒。「<?php」 を書くのも面倒なんだけど。「Velocity *1 だったらな」と思う気持が抑えられなくなってきた。

Velocityのいいところ

Velocityでは、#の後から改行までに制御構文を書ける。改行までってのがポイントで、閉じなくていいってことになる。

そこで、phpでVelocity風のテンプレートエンジンPhelocityを書いてみた。(なんか、1年半前くらいの5番煎じくらいだけど)

仕様

仕様は小さい方がいいので、下のものだけ。(あんまり厳密にVelocityを再現しようとすると手間がかかるし、無駄な記述も増えてしまうので)

  • 「# 」(#のあとに半角スペース)があったら、行末までをphpスクリプトと解釈する。
  • 「#{…..}」があったら、…..の部分をphpスクリプトと解釈する。
  • 「#h{…..}」があったら、…..の部分をphpスクリプトとして解釈した結果をHTMLエスケープして出力する。
  • テンプレートに記述された変数の解決は、Phelocityに渡されたコンテキスト、$_REQUEST、$_SESSION、$_SERVER、$_COOKIEの順で検索される。

使い方

index.php

リクエストされるファイル。Phelocityを使う。

<?phpsession_start();require_once('Phelocity.php');$context = array(
'title'=>'Example',
'list'=>array(10, '<A&#38;B>', NULL));$_SESSION['greeting'] = 'helo';$phelocity = new Phelocity($context);$phelocity->merge('template.html');?>
template.html
<html><head><title>Pelocity</title></head><body><h1>#h{$title}</h1><ul>
# foreach ($list as $e) {<li>#h{$e}</li>
# }</ul>
#h{$greeting}</body></html>

Phelocity本体

Phelocity.php
<?phpdefine('PHELOCITY_TEMPLATE_DIR', 'tmp');/*** Velocity風テンプレートエンジン** 使い方*  - 使用する側*     require_once('Phelocity.php');*     $context = array('title'=>'Example',*                      'list'=>array(10, '<A&#38;B>', NULL));*     $phelocity = new Phelocity($context);*     $phelocity->merge('template.html');*  - テンプレート*  <h1>#h{$title}</h1>*  <ul>*  # foreach ($list as $e) {*      #h{$e}*  # }*  </ul>* @author smeghead*/class Phelocity {private $context = null;/*** コンストラクタ* @param 連想配列 $values 展開する値の連想配列*/function __construct($values = array()) {$this->context = (array)$values;}/*** 値を設定する。* @param string $key キー* @param object $value 値*/function set($key, $value) {$this->context[$key] = $value;}/*** 特殊文法を変換し、実行する。* @param string $template_filename テンプレートファイル名*/function merge($template_filename) {if (!file_exists(PHELOCITY_TEMPLATE_DIR)) {die('ERROR: not exists temporaty directory.');}$temporary_filename = PHELOCITY_TEMPLATE_DIR . "/$template_filename";if (!file_exists($temporary_filename) || filemtime($template_filename) > filemtime($temporary_filename)) {//テンプレートが更新されている場合のみコンパイル作業を行なう。$this->compile($template_filename, $temporary_filename);}//変数の展開 EXTR_SKIPで上書きしないようにしているので、最初の方が優先順位が高い。extract($this->context, EXTR_SKIP);extract($_REQUEST, EXTR_SKIP);if (isset($GLOBALS['_SESSION'])) {extract($_SESSION, EXTR_SKIP);}extract($_SERVER, EXTR_SKIP);extract($_COOKIE, EXTR_SKIP);if (preg_match('/[:\0]/', $temporary_filename)) {//不正なファイルを開かないようにするため。die('invalid filename.' . $temporary_filename);}//Phelocityの処理時には、Undefined Indexの警告は出て欲しくない。$warning_level = error_reporting(E_ERROR | E_WARNING | E_PARSE);require($temporary_filename);error_reporting($warning_level);}/*** テンプレートファイル名を解析して、純粋なphpに変換し一時ファイルに保存する。* @param string $template_filename テンプレートファイル名* @param string $temporary_filename 一時ファイル名*/function compile($template_filename, $temporary_filename) {$template_str = file_get_contents($template_filename);$template_str = preg_replace('/# (.*?)[\r\n]/', '<?php $1 ?>' . PHP_EOL, $template_str);$template_str = preg_replace('/#{([^}]*)}/', '<?php $1 ?>', $template_str);$template_str = preg_replace('/#h{([^}]*)}/', '<?php print htmlspecialchars($1, ENT_QUOTES) ?>', $template_str);$pathinfos = pathinfo($temporary_filename);
@mkdir($pathinfos['dirname']); // 既に存在している場合は、警告を無視する。file_put_contents($temporary_filename, $template_str, LOCK_EX)or die('failed to open file.' . $temporary_filename);}}?>

デメリット

  • #に副作用が出るのを気にする必要がある。
  • NetBeansのサポートが受けられなくなる。

Phelocityって言ってみたかっただけ。

*1:javaのテンプレートエンジン

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です


reCaptcha の認証期間が終了しました。ページを再読み込みしてください。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください