diff --git a/compile.php b/compile.php
new file mode 100644
index 0000000..53297b1
--- /dev/null
+++ b/compile.php
@@ -0,0 +1,29 @@
+
+
+
+
+ Document
+
+
+
+ $vo): $i++; echo htmlentities($vo); endforeach; ?>
+ 将上面的内容放入raw标签:
+
+
+ {$vo}
+
+
+
+ {123456|md5}
+
+ 扩展的标签:
+
+
+
+ 默认内容,没有定义就不会覆盖我
+
+
+ FOOTER
+
+
+
\ No newline at end of file
diff --git a/d237065faa8706d7b5514124a675e4e8.php b/d237065faa8706d7b5514124a675e4e8.php
deleted file mode 100644
index fc870f9..0000000
--- a/d237065faa8706d7b5514124a675e4e8.php
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
- Document
-
-
-
- $vo): $i++; echo ($vo); endforeach; ?>
- 将上面的内容放入raw标签:
-
-
- <$vo>
-
-
-
- 扩展的标签:
-
-
-
- 默认内容,没有定义就不会覆盖我
-
-
- FOOTER
-
-
-
\ No newline at end of file
diff --git a/demo/index.html b/demo/index.html
deleted file mode 100644
index c7bf86d..0000000
--- a/demo/index.html
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-Document
-
-
-
- {$vo}
-
- 将上面的内容放入raw标签:
-
-
- {$vo}
-
-
- {$a}
-
- {:time()}
-
- {:time()}
-
-
- 扩展的标签:
-
-
-
-
- FOOTER
-
\ No newline at end of file
diff --git a/engine/Article.php b/engine/Article.php
new file mode 100644
index 0000000..09218d4
--- /dev/null
+++ b/engine/Article.php
@@ -0,0 +1,18 @@
+ ['attr' => 'category-id', 'close' => 0]
+ ];
+
+ public function _list($attr)
+ {
+ $param = $this->parseDotSyntax($attr['category-id']);
+ return "";
+ }
+}
diff --git a/engine/Engine.php b/engine/Engine.php
index 8785fdd..b8e4693 100644
--- a/engine/Engine.php
+++ b/engine/Engine.php
@@ -1,19 +1,19 @@
';
/**
- * @var array 标签定义
+ * @var array 模板配置
*/
- protected $tags = [];
+ private $config = [];
/**
- * @var null 模板配置
+ * @var array 标签库
*/
- protected $config = null;
+ private $libs = [];
/**
- * @var null 扩展标签库
+ * @var array 标签库类实例
*/
- private $extend = [];
+ private $libInstance = [];
/**
- * @var array 扩展标签库类实例
+ * 获取道单一实例
+ * @param $config
+ * @return Engine
*/
- private $extendInstance = [];
-
- /**
- * @var array 默认标签定义
- */
- private $defaultTags = [
- 'php' => ['attr' => null, 'close' => 1],
- 'if' => ['attr' => 'condition', 'close' => 1],
- 'else' => ['attr' => 'condition', 'close' => 0],
- 'volist' => ['attr' => 'name,index,id,key', 'close' => 1],
- ];
+ public static function instance($config)
+ {
+ if (!self::$instance) {
+ self::$instance = new self($config);
+ }
+ return self::$instance;
+ }
/**
* 构造方法
* Engine constructor.
- * @throws \Exception
+ * @param array $config
*/
- private function __construct()
+ private function __construct($config = [])
{
- // $this->config = Register::get('Config')->get('view');
- $this->config['dir'] = './demo/';
+ $this->config = $config;
if (isset($this->config['left']) && $this->config['left']) {
$this->left = $this->config['left'];
}
@@ -72,18 +69,6 @@ class Engine
}
}
- /**
- * 获取类单一实例
- * @return null|Engine
- */
- public static function instance()
- {
- if (!self::$instance) {
- self::$instance = new self();
- }
- return self::$instance;
- }
-
/**
* 处理模板继承
* @param $template
@@ -91,13 +76,13 @@ class Engine
*/
private function parseExtend($template)
{
- $pattern = '/' . $this->left . 'extend.*?file=[\'"](.*?)[\'"].*?\/' . $this->right . '/is';
+ $pattern = '/' . $this->left . 'extend\s+file[\s\S]*?=[\s\S]*?[\'"](.*?)[\'"][\s\S]*?\/' . $this->right . '/is';
preg_match($pattern, $template, $matches);
if (!empty($matches)) {
- $blockPattern = '/' . $this->left . 'block.*?name=[\'"](.*?)[\'"]' . $this->right;
+ $blockPattern = '/' . $this->left . 'block\s+name[\s\S]*?=[\s\S]*?[\'"](.*?)[\'"][\s\S]*?' . $this->right;
$blockPattern .= '([\s\S]*?)' . $this->left . '\/block' . $this->right . '/is';
// 获得被继承的模板内容
- $file = $this->config['dir'] . $matches[1] . '.html';
+ $file = $this->config['dir'] . $matches[1] . '.' . ltrim($this->config['ext'], '.');
$extendFileContent = null;
if (file_exists($file)) {
$extendFileContent = file_get_contents($file);
@@ -144,10 +129,10 @@ class Engine
*/
private function parseInclude($template)
{
- $pattern = '/' . $this->left . 'include.*?file=[\'"](.*?)[\'"].*?\/' . $this->right . '/is';
+ $pattern = '/' . $this->left . 'include\s+file[\s\S]*?=[\s\S]*?[\'"](.*?)[\'"][\s\S]*?\/' . $this->right . '/is';
$template = preg_replace_callback($pattern, function ($result) {
$string = null;
- $file = $this->config['dir'] . $result[1] . '.html';
+ $file = $this->config['dir'] . $result[1] . '.' . ltrim($this->config['ext'], '.');
if (file_exists($file)) {
$string = file_get_contents($file);
}
@@ -167,7 +152,7 @@ class Engine
*/
private function hasInclude($template)
{
- $pattern = '/' . $this->left . 'include.*?file=[\'"](.*?)[\'"].*?\/' . $this->right . '/is';
+ $pattern = '/' . $this->left . 'include\s+file[\s\S]*?=[\s\S]*?[\'"](.*?)[\'"][\s\S]*?\/' . $this->right . '/is';
preg_match($pattern, $template, $matches);
return !empty($matches);
}
@@ -183,78 +168,147 @@ class Engine
$search = [];
$replace = [];
for ($i = 0; $i < count($matches[0]); $i++) {
- $start = substr($matches[1][$i], 0, 1);
- $search[] = $matches[0][$i];
- if ($start == '$') {
- $replace[] = '';
- } elseif ($start == ':') {
+ $start = mb_substr($matches[1][$i], 0, 1, 'utf8');
+ $end = mb_substr($matches[1][$i], -1, null, 'utf8');
+ if ($start == '$') { // 输出变量
+ $search[] = $matches[0][$i];
+ $output = $this->parseParameterOutput($matches[1][$i]);
+ $replace[] = '';
+ } elseif ($start == ':') { // 调用函数
+ $search[] = $matches[0][$i];
$replace[] = '';
- } else {
- $replace[] = $matches[0][$i];
+ } elseif ($start == '@') { // 输出常量
+ $search[] = $matches[0][$i];
+ $replace[] = '';
+ } elseif ($start == '*' && $end == '*') { // 注释
+ $search[] = $matches[0][$i];
+ $replace[] = '';
}
}
- $template = str_replace($search, $replace, $template);
+ if (!empty($search) && !empty($replace)) {
+ $template = str_replace($search, $replace, $template);
+ }
+
return $template;
}
/**
- * 外部加载扩展标签
- * @param $lib
+ * 解析变量输出
+ * @param $output
+ * @return string
*/
- public function loadTaglib($lib)
+ private function parseParameterOutput($output)
{
- $this->extend[] = $lib;
+ // 处理|函数调用
+ if (strstr($output, '|')) {
+ $functions = explode('|', $output);
+ $parse = $functions[0];
+ // 只留下函数表达式
+ unset($functions[0]);
+ // 重置调用函数数组索引以便开始foreach循环
+ $functions = array_values($functions);
+ foreach ($functions as $function) {
+ $expParameters = explode('=', $function);
+ $functionName = $expParameters[0];
+ // 如果有带上参数,则进行参数处理,没有声明参数则直接将当前值作为函数的第一个参数
+ if (isset($expParameters[1])) {
+ $parameters = $expParameters[1];
+ // 如果有参数,则处理,同时将占位符###替换为上次解析结果
+ // 如果存在占位符,则直接替换,没有占位符则将当前值作为函数的第一个参数
+ if (strstr($expParameters[1], '###')) {
+ $parse = $functionName . '(' . str_replace('###', $parse, $parameters) . ')';
+ } else {
+ $parse = $functionName . '(' . $parse . ',' . $parameters . ')';
+ }
+ } else {
+ $parse = $functionName . '(' . $parse . ')';
+ }
+ }
+ $output = $parse;
+ }
+
+ return $this->parseDotSyntax($output);
}
/**
- * 标签处理
- * @param $template
+ * 处理.语法
+ * @param $string
* @return null|string|string[]
*/
- private function parseTags($template)
+ private function parseDotSyntax($string)
{
- foreach ($this->extend as $lib) {
- $this->extendInstance[$lib] = $object = new $lib;
+ // 处理.语法(仅数组或已实现数组访问接口的对象)
+ return preg_replace_callback("/\.([a-zA-Z0-9_-]*)/", function ($match) {
+ if (isset($match[1])) {
+ return '[' . (is_numeric($match[1]) ? $match[1] : '\'' . $match[1] . '\'') . ']';
+ } else {
+ return null;
+ }
+ }, $string);
+ }
+
+ /**
+ * 外部加载扩展标签
+ * @param $prefix
+ * @param $className
+ */
+ public function loadTaglib($prefix, $className)
+ {
+ if ($prefix == 'default') {
+ throw new \Exception('扩展标签库前缀不能为default');
+ }
+ $this->libs[$prefix] = $className;
+ }
+
+ /**
+ * 获取所有标签
+ * @return null|string|string[]
+ */
+ private function getTags()
+ {
+ $tags = [];
+ // 加入默认标签库
+ $this->libs = array_merge(['default' => Tags::class,], $this->libs);
+ foreach ($this->libs as $prefix => $lib) {
+ $this->libInstance[$prefix] = $object = new $lib;
foreach ($object->tags as $name => $tag) {
- if (!isset($this->tags[$name]) && !isset($this->defaultTags[$name])) {
- $this->tags[$name] = $tag;
+ if (!isset($tags[$name])) { // 如果不存在则加入到标签库
+ $tags[($prefix == 'default' ? '' : $prefix . ':') . $name] = $tag;
}
}
}
- $tags = array_merge($this->defaultTags, $this->tags);
- return $this->_parseTags($template, $tags);
+ return $tags;
}
/**
* 获取标签处理结果
- * @param $method
- * @param $tag
+ * @param $name
+ * @param $attr
* @param string $content
- * @return null
+ * @return mixed
*/
- private function getTagParseResult($method, $tag, $content = '')
+ private function getTagParseResult($name, $attr, $content = '')
{
- if (method_exists($this, $method)) {
- return $this->{$method}($tag, $content);
- } else {
- foreach ($this->extendInstance as $item) {
- if (method_exists($item, $method)) {
- return $item->{$method}($tag, $content);
- }
+ // 如果是扩展标签则找到扩展类进行处理
+ if (strstr($name, ':')) {
+ $tagInfo = explode(':', $name);
+ if (method_exists($this->libInstance[$tagInfo[0]], '_' . $tagInfo[1])) {
+ return $this->libInstance[$tagInfo[0]]->{'_' . $tagInfo[1]}($attr, $content);
}
- return null;
+ } // 否则尝试默认标签处理
+ else if (method_exists($this->libInstance['default'], '_' . $name)) {
+ return $this->libInstance['default']->{'_' . $name}($attr, $content);
}
}
/**
* 进行标签处理
* @param $template
- * @param $tags
* @return null|string|string[]
*/
- private function _parseTags($template, $tags)
+ private function parseTags($template)
{
- foreach ($tags as $name => $item) {
+ foreach ($this->getTags() as $name => $item) {
$pattern = '/' . $this->left . '(?:(' . $name . ')\b(?>[^' . $this->right . ']*)|\/(' . $name . '))';
$pattern .= $this->right . '/is';
if ($item['close']) {
@@ -284,12 +338,15 @@ class Engine
if (!empty($nodes)) {
$nodes = array_merge($nodes, []);
$cut = '';
- $method = '_' . $name;
$startArray = [];
foreach ($nodes as $pos => $node) {
$attr = $item['attr'] ? $this->getAttr($node['start_str'], explode(',', $item['attr'])) : [];
// 得到准备替换的值
- $replace = explode($cut, $this->getTagParseResult($method, $attr, $cut));
+ $replace = explode($cut, $this->getTagParseResult($name, $attr, $cut));
+ $replace = [
+ (isset($replace[0])) ? $replace[0] : [],
+ (isset($replace[1])) ? $replace[1] : [],
+ ];
while ($startArray) {
$begin = end($startArray);
// 如果当前结束位置大于最后一个开始标签的位置,则跳过,直接去替换这个结束标签
@@ -308,7 +365,7 @@ class Engine
'string' => $replace[0]
];
}
- // 替换掉最后入栈,未进入while循环的开始标签
+ // 替换没有结束标签穿插的开始标签
while ($startArray) {
$begin = array_pop($startArray);
$template = substr_replace($template, $begin['string'], $begin['start'], $begin['length']);
@@ -316,14 +373,16 @@ class Engine
}
}
} else { // 自闭合标签处理
- $template = preg_replace_callback($pattern, function ($matches) use ($item) {
- $method = '_' . $matches[1];
- $attr = $item['attr'] ? $this->getAttr($matches[0], explode(',', $item['attr'])) : [];
- return $this->getTagParseResult($method, $attr);
+ $template = preg_replace_callback($pattern, function ($matches) use ($name, $item) {
+ if (!isset($matches[2])) {
+ $attr = $item['attr'] ? $this->getAttr($matches[0], explode(',', $item['attr'])) : [];
+ return $this->getTagParseResult($name, $attr);
+ }
+ return $matches[0];
}, $template);
}
}
- return preg_replace('/\?>([\r|\n|\s]*?)<\?php/is', '', $template);
+ return $template;
}
/**
@@ -335,13 +394,13 @@ class Engine
private function getAttr($string, $tags = [])
{
$attr = [];
- $attrPattern = '/[ +](.*?)=[\'"](.*?)[\'"]/is';
+ $attrPattern = '/\s+(.*?)=[\s\S]*?([\'"])(.*?)\\2/is';
preg_match_all($attrPattern, $string, $result);
if (isset($result[0]) && !empty($result[0])) {
foreach ($result[1] as $key => $value) {
- $name = trim($value, ' ');
+ $name = str_replace([' ', "\t", PHP_EOL], '', $value);
if (in_array($name, $tags)) {
- $attr[$name] = $result[2][$key];
+ $attr[$name] = $result[3][$key];
}
}
}
@@ -349,97 +408,39 @@ class Engine
}
/**
- * 处理raw标签
+ * 处理original标签
* @param $template
* @return null|string|string[]
*/
- private function parseRaw($template)
+ private function parseOriginal($template)
{
- $pattern = '/' . $this->left . 'raw' . $this->right . '([\s\S]*?)';
- $pattern .= $this->left . '\/raw' . $this->right . '/is';
- $template = preg_replace_callback($pattern, function ($matches) {
+ $pattern = '/' . $this->left . 'original' . $this->right . '([\s\S]*?)';
+ $pattern .= $this->left . '\/original' . $this->right . '/is';
+ return preg_replace_callback($pattern, function ($matches) {
return str_replace([
$this->left, $this->right,
'{', '}'
], [
- '',
- ''
+ '',
+ ''
], $matches[1]);
}, $template);
- return $template;
}
/**
- * 还原raw
+ * 还原original内容
* @param $template
* @return mixed
*/
- public function returnRaw($template)
+ private function returnOriginal($template)
{
- $template = str_replace([
- '',
- ''
+ return str_replace([
+ '',
+ ''
], [
$this->left, $this->right,
'{', '}'
], $template);
- return $template;
- }
-
- /**
- * php标签开始
- * @param $tag
- * @param $content
- * @return string
- */
- private function _php($tag, $content)
- {
- return '';
- }
-
- /**
- * if标签
- * @param $tag
- * @param $content
- * @return string
- */
- private function _if($tag, $content)
- {
- $parse = '';
- $parse .= $content;
- $parse .= '';
- return $parse;
- }
-
- /**
- * else标签
- * @param $tag
- * @return string
- */
- private function _else($tag)
- {
- if (isset($tag['condition'])) {
- $parse = '';
- } else {
- $parse = '';
- }
- return $parse;
- }
-
- /**
- * volist标签
- * @param $tag
- * @param $content
- * @return string
- */
- private function _volist($tag, $content)
- {
- $parse = '' : '') . '$' . $tag['id'] . '): ';
- $parse .= (isset($tag['key']) ? '$' . $tag['key'] . '++;' : '') . ' ?>';
- $parse .= $content;
- $parse .= '';
- return $parse;
}
/**
@@ -449,18 +450,22 @@ class Engine
*/
public function compile($template)
{
- // 处理raw标签
- $template = $this->parseRaw($template);
+ // 处理original标签
+ $template = $this->parseOriginal($template);
// 处理模板继承标签
$template = $this->parseExtend($template);
// 处理include标签
$template = $this->parseInclude($template);
- // 处理变量以及函数
- $template = $this->parseVars($template);
// 处理定义的标签
$template = $this->parseTags($template);
+ // 处理变量以及函数
+ $template = $this->parseVars($template);
+ // 还原original内容
+ $template = $this->returnOriginal($template);
+ // 清除多余开始结束标签
+ $template = preg_replace('/\?>([\r|\n|\s]*?)<\?php/is', '', $template);
- return $template;
+ return '' . $template;
}
}
diff --git a/engine/Extend.php b/engine/Extend.php
deleted file mode 100644
index bbff20f..0000000
--- a/engine/Extend.php
+++ /dev/null
@@ -1,16 +0,0 @@
- ['attr' => 'what', 'close' => 0]
- ];
-
- public function _say($tag)
- {
- return '';
- }
-
-}
diff --git a/engine/TagLib.php b/engine/TagLib.php
new file mode 100644
index 0000000..eb89e44
--- /dev/null
+++ b/engine/TagLib.php
@@ -0,0 +1,23 @@
+ ['attr' => '', 'close' => 1],
+ 'if' => ['attr' => 'condition', 'close' => 1],
+ 'else' => ['attr' => 'condition', 'close' => 0],
+ 'loop' => ['attr' => 'from,to,index,key', 'close' => 1],
+ 'assign' => ['attr' => 'name,value', 'close' => 0],
+ 'switch' => ['attr' => 'name', 'close' => 1],
+ 'case' => ['attr' => 'value', 'close' => 1],
+ ];
+
+
+ /**
+ * php标签
+ * @param $attr
+ * @param $content
+ * @return string
+ */
+ public function _php($attr, $content)
+ {
+ return '';
+ }
+
+ /**
+ * if标签
+ * @param $attr
+ * @param $content
+ * @return string
+ */
+ public function _if($attr, $content)
+ {
+ $attr['condition'] = $this->_parseCondition($attr['condition']);
+ $parse = '';
+ $parse .= $content;
+ $parse .= '';
+ return $parse;
+ }
+
+ /**
+ * else标签
+ * @param $attr
+ * @return string
+ */
+ public function _else($attr)
+ {
+ if (isset($attr['condition'])) {
+ $attr['condition'] = $this->_parseCondition($attr['condition']);
+ $parse = '';
+ } else {
+ $parse = '';
+ }
+ return $parse;
+ }
+
+ /**
+ * loop标签
+ * @param $attr
+ * @param $content
+ * @return string
+ */
+ public function _loop($attr, $content)
+ {
+ $parse = '' : '') . '$' . $attr['to'] . '): ';
+ $parse .= (isset($attr['key']) ? '$' . $attr['key'] . '++;' : '') . ' ?>';
+ $parse .= $content;
+ $parse .= '';
+ return $parse;
+ }
+
+ /**
+ * 模版变量赋值
+ * @param $attr
+ * @return string
+ */
+ public function _assign($attr)
+ {
+ if (isset($attr['name']) && isset($attr['value'])) {
+ $quot = (strstr($attr['value'], '\'')) ? '"' : '\'';
+ $parse = '';
+ return $parse;
+ }
+ throw new \Exception('assign标签必须拥有属性:' . $this->defaultTags['assign']['attr']);
+ }
+
+ /**
+ * switch标签
+ * @param $attr
+ * @param $content
+ * @return string
+ */
+ public function _switch($attr, $content)
+ {
+ if (isset($attr['name'])) {
+ $parse = '';
+ $parse .= $content;
+ $parse .= '';
+ return $parse;
+ }
+ throw new \Exception('switch标签必须拥有属性:' . $this->defaultTags['switch']['attr']);
+ }
+
+ /**
+ * switch标签case处理
+ * @param $attr
+ * @param $content
+ * @return string
+ */
+ public function _case($attr, $content)
+ {
+ if (isset($attr['value'])) {
+ $quot = (strstr($attr['value'], '\'')) ? '"' : '\'';
+ $parse = '';
+ $parse .= $content;
+ $parse .= '';
+ } else {
+ $parse = '';
+ $parse .= $content;
+ }
+ return $parse;
+ }
+
+ /**
+ * 处理if/else标签的条件比较符
+ * @param $condition
+ * @return mixed
+ */
+ private function _parseCondition($condition)
+ {
+ return str_ireplace([
+ ' eq ',
+ ' neq ',
+ ' lt ',
+ ' elt ',
+ ' gt ',
+ ' egt ',
+ ' heq ',
+ ' nheq '
+ ], [
+ ' == ',
+ ' != ',
+ ' < ',
+ ' <= ',
+ ' > ',
+ ' >= ',
+ ' === ',
+ ' !== '
+ ], $condition);
+ }
+}
diff --git a/script.php b/script.php
index 6302b64..d71c8b5 100644
--- a/script.php
+++ b/script.php
@@ -1,17 +1,28 @@
loadTaglib(\engine\Extend::class);
-// 编译
-$result = $template->compile($content);
-// 最后还原raw标签
-$result = $template->returnRaw($result);
-$t2 = microtime(true);
-echo '执行时间' . ($t2 - $t1) . "\r\n";
-file_put_contents(md5(time()) . '.php', $result);
\ No newline at end of file
+// 获取模板引擎实例
+$config = [
+ 'ext' => 'html',
+ 'dir' => './view/',
+ 'left' => '<',
+ 'right' => '>',
+];
+$engine = Engine::instance($config);
+// 加载自定义标签库
+$engine->loadTaglib('article', Article::class);
+// 读取模板内容
+$template = file_get_contents('./view/index.html');
+// 编译并写入
+$content = $engine->compile($template);
+file_put_contents('compile.php', $content);
+
+$t2 = microtime(true);
+
+echo '运行时间:' . ($t2 - $t1);
diff --git a/demo/base.html b/view/base.html
similarity index 100%
rename from demo/base.html
rename to view/base.html
diff --git a/demo/footer.html b/view/footer.html
similarity index 100%
rename from demo/footer.html
rename to view/footer.html
diff --git a/view/index.html b/view/index.html
new file mode 100644
index 0000000..8de8809
--- /dev/null
+++ b/view/index.html
@@ -0,0 +1,30 @@
+
+
+Document
+
+
+
+ {$vo}
+
+ 将上面的内容放入raw标签:
+
+
+ {$vo}
+
+
+
+ {123456|md5}
+ {$a|mb_substr=0,1}
+
+ {:time()}
+
+ {:time()}
+
+
+ 扩展的标签:
+
+
+
+
+ FOOTER
+