235 lines
No EOL
7.6 KiB
PHP
235 lines
No EOL
7.6 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|includeCached) (.*\.html)(.*)}}/", $contents, $matches);
|
|
for ($i = 0; $i < count($matches[1]); $i++) {
|
|
$matchString = $matches[0][$i];
|
|
$method = $matches[1][$i];
|
|
$match = $matches[2][$i];
|
|
$options = trim($matches[3][$i]);
|
|
|
|
if ($method === "includeCached") {
|
|
$crc = hash("crc32b", $match . $options);
|
|
$include = Cache::getIncludeCache($crc);
|
|
if (!is_null($include)) {
|
|
$contents = str_replace($matchString, $include, $contents);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
$include = new Template(dirname($this->template) . "/" . $match, $this->data);
|
|
$rendered = $include->render(
|
|
!empty($options) ? json_decode($options, false, 512, JSON_THROW_ON_ERROR) : null
|
|
);
|
|
|
|
if ($method === "includeCached") {
|
|
Cache::setIncludeCache($crc, $rendered);
|
|
}
|
|
|
|
$contents = str_replace($matchString, $rendered, $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);
|
|
}
|
|
|
|
public function saveText(string $filename, string $contents)
|
|
{
|
|
Logger::log("Saving text to " . $filename, Logger::IOWRITE, "Template");
|
|
|
|
file_put_contents(PUBLIC_DIR . "/" . $filename, $contents);
|
|
}
|
|
|
|
} |