Template-engine/template/Engine.php

428 lines
12 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace template;
/**
* 模板标签库(支持模板继承)
* Class Engine
* @package template
*/
class Engine
{
/**
* @var string 左定界符
*/
private $left = '<';
/**
* @var string 右定界符
*/
private $right = '>';
/**
* @var array 标签定义
*/
protected $tags = [];
/**
* @var null 模板配置
*/
protected $config = null;
/**
* @var null 模板路径
*/
private $viewDir = null;
/**
* @var array 默认标签定义
*/
private $defaultTags = [
'if' => ['attr' => 'condition', 'close' => 1],
'else' => ['attr' => 'condition', 'close' => 0],
'volist' => ['attr' => 'name,id,key', 'close' => 1],
'assign' => ['attr' => 'name,value', 'close' => 0],
'switch' => ['attr' => 'name', 'close' => 1],
'case' => ['attr' => 'value', 'close' => 1],
'default' => ['attr' => '', 'close' => 1]
];
/**
* 构造方法
* Engine constructor.
* @throws \Exception
*/
public function __construct()
{
// 仅作示例,请在外部传入模板目录
$this->viewDir = './demo/';
}
/**
* 处理模板继承
*/
private function parseExtend($tmpl)
{
$pattern = '#' . $this->left . 'extend +file=[\'"](.*?)[\'"] +/' . $this->right . '#';
preg_match($pattern, $tmpl, $matches);
if (!empty($matches)) {
$blockPattern = '#' . $this->left . 'block +name=[\'"](.*?)[\'"]' . $this->right;
$blockPattern .= '([\s\S]*?)' . $this->left . '\/block' . $this->right . '#';
// 获得被继承的模板内容
$file = $this->viewDir . $matches[1] . '.html';
$extendFileContent = null;
if (file_exists($file)) {
$extendFileContent = file_get_contents($file);
}
// 处理继承中的include标签
$tempContent = $this->parseInclude($extendFileContent);
$extendFileContent = $tempContent !== false ? $tempContent : $extendFileContent;
// 被继承模板中的块
preg_match_all($blockPattern, $extendFileContent, $extendResult);
// 继承模板中的块
preg_match_all($blockPattern, $tmpl, $templateResult);
// 组合搜索的块数组
$search = [];
$defaultContent = [];
for ($i = 0; $i < count($extendResult[0]); $i++) {
$search[$extendResult[1][$i]] = $extendResult[0][$i];
$defaultContent[$extendResult[1][$i]] = $extendResult[2][$i];
}
// 组合替换的块数组
$replace = [];
for ($j = 0; $j < count($templateResult[0]); $j++) {
$replace[$templateResult[1][$j]] = $templateResult[2][$j];
}
// 块是否在继承模板中存在
$searchArray = [];
$replaceArray = [];
foreach ($search as $key => $value) {
$searchArray[] = $value;
if (isset($replace[$key])) {
$replaceArray[] = $replace[$key];
} else {
$replaceArray[] = $defaultContent[$key];
}
}
$tmpl = str_replace($searchArray, $replaceArray, $extendFileContent);
}
return $tmpl;
}
/**
* 处理include标签
* @return bool
*/
private function parseInclude($tmpl)
{
$pattern = '#' . $this->left . 'include +file=[\'"](.*?)[\'"] +/' . $this->right . '#';
$tmpl = preg_replace_callback($pattern, function ($result) {
$str = null;
$file = $this->viewDir . $result[1] . '.html';
if (file_exists($file)) {
$str = file_get_contents($file);
}
return $str;
}, $tmpl);
// 处理多层include
if ($this->hasInclude($tmpl)) {
$tmpl = $this->parseInclude($tmpl);
}
return $tmpl;
}
/**
* 检测是否含有include
* @param $tmpl
* @return bool
*/
private function hasInclude($tmpl)
{
$pattern = '#' . $this->left . 'include +file=[\'"](.*?)[\'"] +/' . $this->right . '#';
preg_match($pattern, $tmpl, $matches);
return !empty($matches);
}
/**
* 分析参数以及函数输出
*/
private function parseVars($tmpl)
{
preg_match_all('#{(.*?)}#', $tmpl, $matches);
$search = [];
$replace = [];
for ($i = 0; $i < count($matches[0]); $i++) {
$start = substr($matches[1][$i], 0, 1);
if ($start == '$') {
$search[] = $matches[0][$i];
$replace[] = '<?php echo ' . $matches[1][$i] . '; ?>';
} elseif ($start == ':') {
$search[] = $matches[0][$i];
$replace[] = '<?php echo ' . ltrim($matches[1][$i], ':') . '; ?>';
}
}
$tmpl = str_replace($search, $replace, $tmpl);
return $tmpl;
}
/**
* 处理用户自定义标签
* @param $tmpl
* @return null|string|string[]
*/
public function parseCustomizeTags($tmpl)
{
return $this->parseTags($tmpl, $this->tags);
}
/**
* 处理默认的标签
* @param $tmpl
* @return null|string|string[]
*/
private function parseDefaultTags($tmpl)
{
return $this->parseTags($tmpl, $this->defaultTags);
}
/**
* 进行标签处理
* @param $tmpl
* @param $tags
* @return null|string|string[]
*/
private function parseTags($tmpl, $tags)
{
foreach ($tags as $name => $item) {
$attr = ($item['attr']) ? ' +(.*?)' : null;
$pattern = '#' . $this->left . $name . $attr . ($item['close'] ? $this->right : '\/' . $this->right . '#');
if ($item['close']) {
$pattern .= '([\s\S]*?)' . $this->left . '\/' . $name . $this->right . '#';
}
preg_match_all($pattern, $tmpl, $matches);
for ($i = 0; $i < count($matches[0]); $i++) {
$attrPattern = '#(.*?)=[\'"](.*?)[\'"]#';
preg_match_all($attrPattern, $matches[1][$i], $result);
$tag = [];
if (!empty($result)) {
foreach ($result[1] as $key => $value) {
$tag[trim($value, ' ')] = $result[2][$key];
}
}
if ($item['close']) {
$tagContent = isset($matches[2][$i]) ? $matches[2][$i] : (($attr) ? null : $matches[1][$i]);
$content = $this->{'_' . $name . '_start'}($tag, $tagContent);
if ($item['close']) {
$content .= $this->{'_' . $name . '_end'}($tag, $tagContent);
}
} else {
$content = $this->{'_' . $name . '_start'}($tag);
}
$tmpl = str_replace($matches[0][$i], $content, $tmpl);
}
}
return preg_replace('#\?>([\r|\n|\s]*?)<\?php#', '', $tmpl);
}
/**
* 处理raw标签
* @param $tmpl
* @return null|string|string[]
*/
private function parseRaw($tmpl)
{
$pattern = '#' . $this->left . 'raw' . $this->right . '([\s\S]*?)';
$pattern .= $this->left . '\/raw' . $this->right . '#';
$tmpl = preg_replace_callback($pattern, function ($matches) {
return str_replace([
$this->left, $this->right,
'{', '}'
], [
'<raw!--', '--raw>',
'{raw!--', '--raw}'
], $matches[1]);
}, $tmpl);
return $tmpl;
}
/**
* 还原raw
* @param $tmpl
* @return null|string|string[]
*/
public function returnRaw($tmpl)
{
$pattern = '#[{|<]raw!--([\s\S]*?)--raw[>|}]#';
$tmpl = preg_replace_callback($pattern, function ($matches) {
$left = substr($matches[0], 0, 1);
$right = substr($matches[0], -1);
return $left . $matches[1] . $right;
}, $tmpl);
return $tmpl;
}
/**
* if标签
* @param $tag
* @param $content
* @return string
*/
private function _if_start($tag, $content)
{
return '<?php if (' . $tag['condition'] . '): ?>' . $content;
}
/**
* if标签结束
* @return string
*/
private function _if_end()
{
return '<?php endif; ?>';
}
/**
* else标签支持条件
* @param $tag
* @return string
*/
private function _else_start($tag)
{
if (isset($tag['condition'])) {
$parse = '<?php elseif (' . $tag['condition'] . '): ?>';
} else {
$parse = '<?php else: ?>';
}
return $parse;
}
/**
* volist标签
* @param $tag
* @param $content
* @return string
*/
private function _volist_start($tag, $content)
{
if (substr($tag['name'], 0, 1) == ':') {
$name = substr($tag['name'], 1);
} else {
$name = '$' . $tag['name'];
}
$key = (empty($tag['key'])) ? null : '$' . $tag['key'] . ' = 0;';
$parse = '<?php ' . $key . ' foreach (' . $name . ' as $' . $tag['id'] . '): ';
$parse .= ($key ? '$' . $tag['key'] . '++;' : null) . ' ?>' . $content;
return $parse;
}
/**
* volist标签结束
* @return string
*/
private function _volist_end()
{
return '<?php endforeach; ?>';
}
/**
* assign标签
* @param $tag
* @return string
*/
private function _assign_start($tag)
{
return '<?php $' . $tag['name'] . ' = ' . $tag['value'] . '; ?>';
}
/**
* switch标签
* @param $tag
* @param $content
* @return string
*/
private function _switch_start($tag, $content)
{
$parse = '<?php switch($' . $tag['name'] . '): ?>' . $content;
return $parse;
}
/**
* switch标签结束
* @return string
*/
private function _switch_end()
{
$parse = '<?php endswitch; ?>';
return $parse;
}
/**
* case标签
* @param $tag
* @param $content
* @return string
*/
private function _case_start($tag, $content)
{
if (is_numeric($tag['value'])) {
$caseVar = $tag['value'];
} else {
$caseVar = '\'' . $tag['value'] . '\'';
}
$parse = '<?php case ' . $caseVar . ': ?>' . $content;
return $parse;
}
/**
* case标签结束
* @return string
*/
private function _case_end()
{
$parse = '<?php break; ?>';
return $parse;
}
/**
* default标签
* @param $tag
* @param $content
* @return string
*/
private function _default_start($tag, $content)
{
$parse = '<?php default: ?>' . $content;
return $parse;
}
/**
* default标签结束
* @return string
*/
private function _default_end()
{
$parse = '<?php break; ?>';
return $parse;
}
/**
* 获取编译后的内容
* @param $tmpl
* @return bool|mixed|null|string|string[]
*/
public function compile($tmpl)
{
$tmpl = $this->parseRaw($tmpl);
// 处理模板继承标签
$tmpl = $this->parseExtend($tmpl);
// 处理include标签
$tmpl = $this->parseInclude($tmpl);
// 处理变量以及函数
$tmpl = $this->parseVars($tmpl);
// 处理定义的标签
$tmpl = $this->parseDefaultTags($tmpl);
return $tmpl;
}
}