2
0
mirror of https://github.com/boostorg/website.git synced 2026-01-26 07:02:23 +00:00
Files
website/common/code/boost_simple_template.php
2016-05-11 09:13:01 +01:00

178 lines
5.6 KiB
PHP

<?php
require_once(__DIR__.'/boost.php');
/* Simple template library inspired by mustache.
* Does not implement:
*
* Triple brackets for unescaped output.
* Lambdas
* Comments
* Parials
* Set Delimiter
*
* Doesn't claim to be at all compatible with Mustache, just that it should be
* easy to switch to a proper Mustache implementation in the future.
*/
class BoostSimpleTemplate {
static function render($template, $params) {
$parsed_template = self::parse_template($template);
//print_r($params); exit(0);
echo self::interpret($parsed_template, $params);
}
static function parse_template($template) {
preg_match_all('@
{{(?:
[!].*? |
([#/^]?)([\w]+)
)}}
([ #t]*\n)?
@xsm',
$template, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
$template_parts = array();
$operator_stack = array();
$scope_stack = array();
$template_stack = array();
$last_offset = 0;
foreach($matches as $match) {
$text_offset = $last_offset;
$text_length = $match[0][1] - $text_offset;
$last_offset = $match[0][1] + strlen($match[0][0]);
if ($text_length) {
$template_parts[] = substr($template, $text_offset, $text_length);
}
$operator = null;
if (!empty($match[2][0])) {
$operator = $match[1][0] ?: '$';
$symbol = $match[2][0];
}
switch($operator) {
case null:
break;
case '#':
case '^':
$operator_stack[] = $operator;
$scope_stack[] = $symbol;
$template_stack[] = $template_parts;
$template_parts = array();
break;
case '/':
if (array_pop($scope_stack) !== $symbol) {
// TODO: Better error message here.
echo "Template error.\n";
exit(1);
}
$parent_template_parts = array_pop($template_stack);
$parent_template_parts[] = array(
'type' => array_pop($operator_stack),
'symbol' => $symbol,
'contents' => $template_parts,
);
$template_parts = $parent_template_parts;
break;
case '$':
$template_parts[] = array(
'type' => $operator,
'symbol' => $symbol,
);
break;
default:
assert(false);
exit(1);
}
}
if ($scope_stack) {
// TODO: Better error message here.
echo "Template error.\n";
exit(1);
}
$end = substr($template, $last_offset);
if ($end) { $template_parts[] = $end; }
return $template_parts;
}
static function interpret($template_array, $params) {
$output = '';
foreach($template_array as $template_part) {
if (is_string($template_part)) {
$output .= $template_part;
} else {
$symbol = $template_part['symbol'];
$value = array_key_exists($symbol, $params) ?
$params[$symbol] : null;
switch($template_part['type']) {
case '$':
if ($value) {
$output .= html_encode($value);
}
break;
case '#':
if ($value) {
$output .= self::interpret_nested_content(
$template_part['contents'],
$params,
$value);
}
break;
case '^':
if (!$value) {
$output .= self::interpret(
$template_part['contents'],
$params);
}
break;
default:
assert(false);
exit(1);
}
}
}
return $output;
}
static function interpret_nested_content($template_array, $params, $value) {
if (is_object($value)) {
return self::interpret($template_array, array_merge($params, (array) $value));
}
if (is_array($value)) {
// Just checking the first key to see if this is a list or object.
// Should probably check every key?
reset($value);
if (is_int(key($value))) {
$output = '';
foreach($value as $x) {
if (is_object($x)) {
$output .= self::interpret($template_array, array_merge($params, (array) $x));
}
else if (is_array($x)) {
$output .= self::interpret($template_array, array_merge($params, $x));
}
else {
// TODO: Better error?
assert(false);
exit(0);
}
}
return $output;
}
else {
return self::interpret($template_array, array_merge($params, $value));
}
}
else {
return self::interpret($template_array, $params);
}
}
}