pohja/lib/Template.php

212 lines
7.0 KiB
PHP

<?php
class Template {
protected $template = null;
protected $data = array();
public function __construct(string $template, ?array $parentData = null) {
$this->template = $template;
if (isset($parentData)) {
$this->data = $parentData;
}
}
public function set(string $key, $value) {
$this->data[$key] = $value;
}
public function render($options = null): string {
Logger::log("Rendering template " . $this->template, Logger::IOREAD, "Template");
$class = is_object($options) && isset($options->class) ? new $options->class($this, $options) : new EmptyTemplate($this, $options);
$class->load();
$contents = Cache::getTemplateFromCache($this->template);
if (is_null($contents)) {
$contents = file_get_contents(TEMPLATE_DIR . "/" . $this->template);
Cache::setTemplateToCache($this->template, $contents);
}
$class->pretransform();
// Replace includes of templates
$matches = array();
preg_match_all("/{{include (.*\.html)(.*)}}/", $contents, $matches);
for ($i = 0; $i < count($matches[1]); $i++) {
$matchString = $matches[0][$i];
$match = $matches[1][$i];
$options = trim($matches[2][$i]);
$include = new Template(dirname($this->template) . "/" . $match, $this->data);
$contents = str_replace($matchString, $include->render(
!empty($options) ? json_decode($options, false, 512, JSON_THROW_ON_ERROR) : null
), $contents);
}
$class->transform();
// Replace variables
foreach ($this->data as $key => $value) {
if (is_string($value) || is_numeric($value)) {
$contents = str_replace("{{" . $key . "}}", $value, $contents);
} else {
$contents = str_replace("{{" . $key . "}}", "", $contents);
}
}
// Conditional tags are written as {{?<variablename>}}some content{{/<variablename>}}, find conditional tags and evaluate them if they exist in $this->data
$matches = array();
preg_match_all("/{{\?(.*)}}(.*){{\/(.*)}}/sU", $contents, $matches);
for ($i = 0; $i < count($matches[1]); $i++) {
$matchString = $matches[0][$i];
$match = $matches[1][$i];
$content = $matches[2][$i];
if (isset($this->data[$match])) {
$contents = str_replace($matchString, $content, $contents);
} else {
$contents = str_replace($matchString, "", $contents);
}
}
$class->save();
return $contents;
}
public function save(string $filename, string $contents, bool $minify = true) {
Logger::log("Saving template to " . $filename, Logger::IOWRITE, "Template");
if ($minify) {
$size = strlen($contents);
$doc = new DOMDocument();
$doc->preserveWhiteSpace = false;
$doc->formatOutput = false;
$doc->normalizeDocument();
libxml_use_internal_errors(true);
$doc->loadHTML($contents);
libxml_use_internal_errors(false);
$options = [
'keep_whitespace_around' => [
// keep whitespace around inline elements
'b', 'big', 'i', 'small', 'tt',
'abbr', 'acronym', 'cite', 'code', 'dfn', 'em', 'kbd', 'strong', 'samp', 'var',
'a', 'bdo', 'br', 'img', 'map', 'object', 'q', 'span', 'sub', 'sup',
'button', 'input', 'label', 'select', 'textarea'
],
'keep_whitespace_in' => [/*'script', 'style',*/ 'pre'],
'remove_empty_attributes' => ['style', 'class'],
'indent_characters' => "\t"
];
//Comments,empty,whitespace
$xpath = new \DOMXPath($doc);
foreach ($xpath->query('//comment()') as $comment) {
$comment->parentNode->removeChild($comment);
}
$xpath = new \DOMXPath($doc);
foreach ($options['remove_empty_attributes'] as $attr) {
foreach ($xpath->query('//*[@' . $attr . ']') as $el) {
if (trim($el->getAttribute($attr)) == '') {
$el->removeAttribute($attr);
}
}
}
$x = new \DOMXPath($doc);
$nodeList = $x->query("//text()");
foreach ($nodeList as $node) {
/** @var \DOMNode $node */
if (in_array($node->parentNode->nodeName, $options['keep_whitespace_in'])) {
continue;
};
$node->nodeValue = str_replace(["\r", "\n", "\t"], ' ', $node->nodeValue);
while (strpos($node->nodeValue, ' ') !== false) {
$node->nodeValue = str_replace(' ', ' ', $node->nodeValue);
}
if (!in_array($node->parentNode->nodeName, $options['keep_whitespace_around'])) {
if (!($node->previousSibling && in_array(
$node->previousSibling->nodeName,
$options['keep_whitespace_around']
))) {
$node->nodeValue = ltrim($node->nodeValue);
}
if (!($node->nextSibling && in_array(
$node->nextSibling->nodeName,
$options['keep_whitespace_around']
))) {
$node->nodeValue = rtrim($node->nodeValue);
}
}
if ((strlen($node->nodeValue) == 0)) {
$node->parentNode->removeChild($node);
}
}
$contents = $doc->saveHTML();
Logger::log("Minified template from " . $size . " to " . strlen($contents) . " bytes", Logger::METRICS, "Template");
}
file_put_contents(PUBLIC_DIR . "/" . $filename, $contents);
}
public function saveCss(string $filename, string $contents, bool $minify = true)
{
Logger::log("Saving css to " . $filename, Logger::IOWRITE, "Template");
if ($minify) {
$size = strlen($contents);
$contents = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $contents);
$contents = str_replace(': ', ':', $contents);
$contents = str_replace(array("\r\n", "\r", "\n", "\t", ' ', ' ', ' '), '', $contents);
Logger::log("Minified " . $filename . " from " . $size . " to " . strlen($contents) . " bytes", Logger::METRICS, "Template");
}
file_put_contents(PUBLIC_DIR . "/" . $filename, $contents);
}
public function saveScript(string $filename, string $contents, bool $minify = true)
{
Logger::log("Saving script to " . $filename, Logger::IOWRITE, "Template");
if ($minify) {
$size = strlen($contents);
$contents = preg_replace('/([-\+])\s+\+([^\s;]*)/', '$1 (+$2)', $contents);
$contents = preg_replace("/\s*\n\s*/", "\n", $contents);
$contents = preg_replace("/\h+/", " ", $contents);
$contents = preg_replace("/\h([^A-Za-z0-9\_\$])/", '$1', $contents);
$contents = preg_replace("/([^A-Za-z0-9\_\$])\h/", '$1', $contents);
$contents = preg_replace("/\s?([\(\[{])\s?/", '$1', $contents);
$contents = preg_replace("/\s([\)\]}])/", '$1', $contents);
$contents = preg_replace("/\s?([\.=:\-+,])\s?/", '$1', $contents);
$contents = preg_replace("/;\n/", ";", $contents);
$contents = preg_replace('/;}/', '}', $contents);
$contents = preg_replace('/\s+/', ' ', $contents);
$contents = preg_replace('/\s*([;{}])\s*/', '$1', $contents);
$contents = preg_replace('/\s*(;{})\s*/', '$1', $contents);
$contents = str_replace(array("\r\n", "\r", "\n", "\t", ' ', ' ', ' '), '', $contents);
Logger::log("Minified " . $filename . " from " . $size . " to " . strlen($contents) . " bytes", Logger::METRICS, "Template");
}
file_put_contents(PUBLIC_DIR . "/" . $filename, $contents);
}
}