diff --git a/demo/base.html b/demo/base.html
new file mode 100644
index 0000000..d8efd58
--- /dev/null
+++ b/demo/base.html
@@ -0,0 +1,14 @@
+
+
+
+
+ TITLE
+
+
+
+
+ 默认内容,没有定义就不会覆盖我
+
+
+
+
\ No newline at end of file
diff --git a/demo/footer.html b/demo/footer.html
new file mode 100644
index 0000000..dd38f0e
--- /dev/null
+++ b/demo/footer.html
@@ -0,0 +1 @@
+include footer
\ No newline at end of file
diff --git a/demo/index.html b/demo/index.html
new file mode 100644
index 0000000..439f66b
--- /dev/null
+++ b/demo/index.html
@@ -0,0 +1,24 @@
+
+
+Document
+
+
+
+ {$vo}
+
+ 将上面的内容放入raw标签:
+
+
+ {$vo}
+
+
+
+ {:time()}
+
+ 扩展的标签:
+
+
+
+
+ FOOTER
+
\ No newline at end of file
diff --git a/script.php b/script.php
new file mode 100644
index 0000000..a3b74d3
--- /dev/null
+++ b/script.php
@@ -0,0 +1,26 @@
+compile($content);
+
+echo "\r\n\r\n第一次结果\r\n\r\n";
+print_r($result);
+
+// 开始处理用户定义标签库
+$extend = new \template\Extend();
+$result = $extend->parseCustomizeTags($result);
+// 用户定义标签库处理结束
+
+// 最后还原raw标签
+$result = $template->returnRaw($result);
+
+echo "\r\n\r\n第二次结果\r\n\r\n";
+print_r($result);
diff --git a/template/Engine.php b/template/Engine.php
new file mode 100644
index 0000000..8d0e4ea
--- /dev/null
+++ b/template/Engine.php
@@ -0,0 +1,427 @@
+';
+
+ /**
+ * @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[] = '';
+ } elseif ($start == ':') {
+ $search[] = $matches[0][$i];
+ $replace[] = '';
+ }
+ }
+ $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}'
+ ], $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 '' . $content;
+ }
+
+ /**
+ * if标签结束
+ * @return string
+ */
+ private function _if_end()
+ {
+ return '';
+ }
+
+ /**
+ * else标签(支持条件)
+ * @param $tag
+ * @return string
+ */
+ private function _else_start($tag)
+ {
+ if (isset($tag['condition'])) {
+ $parse = '';
+ } else {
+ $parse = '';
+ }
+ 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 = '' . $content;
+ return $parse;
+ }
+
+ /**
+ * volist标签结束
+ * @return string
+ */
+ private function _volist_end()
+ {
+ return '';
+ }
+
+ /**
+ * assign标签
+ * @param $tag
+ * @return string
+ */
+ private function _assign_start($tag)
+ {
+ return '';
+ }
+
+ /**
+ * switch标签
+ * @param $tag
+ * @param $content
+ * @return string
+ */
+ private function _switch_start($tag, $content)
+ {
+ $parse = '' . $content;
+ return $parse;
+ }
+
+ /**
+ * switch标签结束
+ * @return string
+ */
+ private function _switch_end()
+ {
+ $parse = '';
+ 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 = '' . $content;
+ return $parse;
+ }
+
+ /**
+ * case标签结束
+ * @return string
+ */
+ private function _case_end()
+ {
+ $parse = '';
+ return $parse;
+ }
+
+ /**
+ * default标签
+ * @param $tag
+ * @param $content
+ * @return string
+ */
+ private function _default_start($tag, $content)
+ {
+ $parse = '' . $content;
+ return $parse;
+ }
+
+ /**
+ * default标签结束
+ * @return string
+ */
+ private function _default_end()
+ {
+ $parse = '';
+ 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;
+ }
+
+}
diff --git a/template/Extend.php b/template/Extend.php
new file mode 100644
index 0000000..77e272f
--- /dev/null
+++ b/template/Extend.php
@@ -0,0 +1,16 @@
+ ['attr' => 'what', 'close' => 0]
+ ];
+
+ protected function _say_start($tag)
+ {
+ return '';
+ }
+
+}