Skip to content

Instantly share code, notes, and snippets.

@yuka2py
Last active December 29, 2015 16:49
Show Gist options
  • Save yuka2py/7700417 to your computer and use it in GitHub Desktop.
Save yuka2py/7700417 to your computer and use it in GitHub Desktop.
PHP 自体をテンプレートエンジンとして利用するための簡単なユーティリティ。Django 風の extend、block、capture、strip などを備えている。
<?php
/**
* echo のエイリアス
* @return void
*/
function e($text) {
echo $text;
}
/**
* htmlspecialchars のエイリアス
* @return string
*/
function h($text) {
return htmlspecialchars($text);
}
/**
* echo + htmlspecialchars のエイリアス
* @return void
*/
function eh($text) {
echo htmlspecialchars($text);
}
/**
* render::display() のエイリアス
* @param string $view ビューファイル
* @param array $vars このレンダリングで使用する変数
* @return void
*/
function render($view, $vars=array()) {
render::display($view, $vars);
}
/**
* PHP 自体をテンプレートエンジンとして利用するための簡単なユーティリティ
*/
class render
{
public static $view_directory = '';
public static $view_file_sufix = '';
private static $vars = array();
private static $extend_view = null;
private static $extend_vars = array();
private static $extend_ancestor_blocks = array();
private static $block_stack = array();
private static $block_contents = array();
/**
* ビューをレンダリングして結果を文字列で取得する。
* @param string $view ビューファイル
* @param array $vars このレンダリングで使用する変数
* @return string
*/
public static function to_string($view, $vars=array()) {
ob_start();
self::display($view, $vars);
return ob_get_clean();
}
/**
* $view をレンダリングして表示する。
* @param string $view ビューファイル
* @param array $vars このレンダリングで使用する変数
* @return void
*/
public static function display($view, $vars=array()) {
$____view = self::get_view_filepath($view);
$____vars = $vars;
extract(self::$vars); //デフォルト指定の変数
extract(self::$extend_vars); //継承による変数
extract($____vars); //ここで指定された変数
require $____view;
}
/**
* ビューファイルのパスを取得する。
* @param string $view ビューファイル
* @return string
*/
private static function get_view_filepath($view) {
return self::$view_directory
. DIRECTORY_SEPARATOR
. $view . self::$view_file_sufix;
}
/**
* レンダラのデフォルト変数を設定する。
* レンダラのデフォルト変数は、全てのレンダリングで利用できる。
* 連想配列、または名前を値を指定して設定できる。
* @param mixed $one
* @param mixed $two
* @return void
*/
public static function setvar($one, $two=null) {
if (is_array($one)) {
self::$vars = array_merge(self::$vars, $one);
} else {
self::$vars[$one] = $two;
}
}
/**
* レンダラのデフォルト変数を取得する。
* @param string $name 変数名
* @param mixed $default 変数が未定義の時に返される値
* @return mixed
*/
public static function getvar($name, $default=null) {
return isset(self::$vars[$name]) ? self::$vars[$name] : $default;
}
/**
* 継承を開始する。
* @param string $view ビューファイル
* @param array $vars 継承するテンプレートで利用したい変数
* @return void
*/
public static function extend($view, $vars=array()) {
self::$extend_view = $view;
self::$extend_vars = array_merge($vars, self::$extend_vars); //開発注:この順番は正しい。self::$extend_vars の方が、より子で指定された値になる
self::capture();
}
/**
* 継承を終了する
* @return void
*/
public static function end_extend() {
$content = self::end_capture();
$view = self::$extend_view;
$vars = self::$extend_vars;
self::$extend_view = null;
self::$block_contents['__content'] = array($content, 'append');
self::display($view, $vars);
}
/**
* 継承の祖先にブロックがあるか確認する
* @param string $block
* @return bool
*/
private static function exists_block_in_ancestors($block) {
if (empty(self::$extend_view)) {
return array();
} else {
$ancestor_blocks = self::get_ancestor_blocks(self::$extend_view);
return in_array($block, $ancestor_blocks);
}
}
/**
* 継承の祖先に含まれるブロック名の一覧を返す。
* @param string $parent 継承したビューファイル
* @return array<string>
*/
private static function get_ancestor_blocks($parent) {
$parent = self::get_view_filepath($parent);
if (!isset(self::$extend_ancestor_blocks[$parent])) {
$blocks = array();
$view = file_get_contents($parent);
if (preg_match_all('/render::(?:block|load_block)\(\s*["\']([^"\']+)["\']\s*/', $view, $matches, PREG_PATTERN_ORDER)) {
$blocks = $matches[1];
}
if (preg_match('/render::extend\(\s*["\']([^"\']+)["\']\s*/', $view, $matches)) {
$more_parent = $matches[1];
$ancestor_blocks = self::get_ancestor_blocks($more_parent);
$blocks = array_merge($blocks, $ancestor_blocks);
$blocks = array_unique($blocks);
}
self::$extend_ancestor_blocks[$parent] = $blocks;
}
return self::$extend_ancestor_blocks[$parent];
}
/**
* ブロックの定義を開始する
* @param string $block ブロックの名前
* @param mixed $method 既にブロック変数が定義済みの場合、このブロックの内容を後ろに追加する場合には 'append' を指定、前に追加する場合には 'prepend' を指定する。デフォルトは false で追加せず上書きする
* @return void
*/
public static function block($block, $method=false) {
array_push(self::$block_stack, array($block, $method));
self::capture();
}
/**
* ブロックの定義を開始する。
* ブロックは継承元ブロックの後ろに追加される。
* @param string $block ブロックの名前
* @return void
*/
public static function block_append($block) {
array_push(self::$block_stack, array($block, 'append'));
self::capture();
}
/**
* ブロックの定義を開始する。
* ブロックは継承元ブロックの前に追加される。
* @param string $block ブロックの名前
* @return void
*/
public static function block_prepend($block) {
array_push(self::$block_stack, array($block, 'prepend'));
self::capture();
}
/**
* ブロックの定義を終了する
* @return string ブロックの内容
*/
public static function end_block() {
$content = self::end_capture();
list($block, $method) = array_pop(self::$block_stack);
//オーバーライドされたブロックがある場合(既にブロックデータが存在した場合)
if (isset(self::$block_contents[$block])) {
list($pre_content, $pre_method) = self::$block_contents[$block];
//前に追加
if ('append' === $pre_method) {
$content = $content . $pre_content;
}
//前に追加
else if ('prepend' === $pre_method) {
$content = $pre_content . $content;
}
//オーバーライド
else {
$content = $pre_content;
}
}
//ブロックデータとして保存
self::$block_contents[$block] = array($content, $method);
//上位にブロックが無いとき、または __content ブロックなら出力
if ($block === '__content' or !self::exists_block_in_ancestors($block)) {
echo $content;
}
return $content;
}
/**
* 登録されているブロックをロードする
* @param string $block ブロックの名前
* @return void
*/
public static function load_block($block) {
render::block($block);
render::end_block();
}
/**
* extend でオーバーライドされたコンテンツをロードする
* @return void
*/
public static function load_content() {
render::block('__content');
render::end_block();
}
/**
* キャプチャーを開始する
* @return void
*/
public static function capture() {
ob_start();
}
/**
* キャプチャーを終了する
* @return string キャプチャした内容
*/
public static function end_capture() {
return ob_get_clean();
}
/**
* 改行やタブを取り除く範囲を開始する
* @return void
*/
public static function strip() {
self::capture();
}
/**
* 改行やタブを取り除く範囲を終了する
* @return void
*/
public static function end_strip() {
$content = self::end_capture();
$content = str_replace("\n", '', $content);
$content = str_replace("\r", '', $content);
$content = str_replace("\t", '', $content);
echo $content;
}
}
@yuka2py
Copy link
Author

yuka2py commented Nov 29, 2013

使い方

require_once 'render.php';
render::$view_directory = '/home/projects/mywebapp/views'; //ビューファイルが配置されるディレクトリ

render('index.html.php');

_partial.html.php

<div class="cute">わたし?</div>

_layout.html.php

<body>

<h1></h1>

original sidevar contents </div>

</body>

index.html.php

'トップページ')) ?>

ほげほーげ。

over ride sidevar contents

OUTPUT

<!DOCTYPE html>
<body>

<h1>トップページ</h1>

ほげほーげ。

<div id="sidevar">
over ride sidevar contents
</div>

<div class="cute">わたし?</div>

</body>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment