diff --git a/2ec7facf63614beb8c7f02cc2378e1cd.php b/d237065faa8706d7b5514124a675e4e8.php
similarity index 68%
rename from 2ec7facf63614beb8c7f02cc2378e1cd.php
rename to d237065faa8706d7b5514124a675e4e8.php
index 60952c6..fc870f9 100644
--- a/2ec7facf63614beb8c7f02cc2378e1cd.php
+++ b/d237065faa8706d7b5514124a675e4e8.php
@@ -6,14 +6,14 @@
-
+ $vo): $i++; echo ($vo); endforeach; ?>
将上面的内容放入raw标签:
- {$vo}
+ <$vo>
-
+
扩展的标签:
diff --git a/demo/index.html b/demo/index.html
index bcb1431..c7bf86d 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -3,7 +3,7 @@
Document
-
+
{$vo}
将上面的内容放入raw标签:
diff --git a/template/Engine.php b/engine/Engine.php
similarity index 56%
rename from template/Engine.php
rename to engine/Engine.php
index 3c8bbdc..8785fdd 100644
--- a/template/Engine.php
+++ b/engine/Engine.php
@@ -1,6 +1,6 @@
['attr' => null, 'close' => 1],
'if' => ['attr' => 'condition', 'close' => 1],
'else' => ['attr' => 'condition', 'close' => 0],
- 'volist' => ['attr' => 'name,id,key', 'close' => 1],
- 'assign' => ['attr' => 'name,value', 'close' => 0]
+ 'volist' => ['attr' => 'name,index,id,key', 'close' => 1],
];
/**
@@ -92,11 +91,11 @@ class Engine
*/
private function parseExtend($template)
{
- $pattern = '#' . $this->left . 'extend +file=[\'"](.*?)[\'"] +/' . $this->right . '#';
+ $pattern = '/' . $this->left . 'extend.*?file=[\'"](.*?)[\'"].*?\/' . $this->right . '/is';
preg_match($pattern, $template, $matches);
if (!empty($matches)) {
- $blockPattern = '#' . $this->left . 'block +name=[\'"](.*?)[\'"]' . $this->right;
- $blockPattern .= '([\s\S]*?)' . $this->left . '\/block' . $this->right . '#';
+ $blockPattern = '/' . $this->left . 'block.*?name=[\'"](.*?)[\'"]' . $this->right;
+ $blockPattern .= '([\s\S]*?)' . $this->left . '\/block' . $this->right . '/is';
// 获得被继承的模板内容
$file = $this->config['dir'] . $matches[1] . '.html';
$extendFileContent = null;
@@ -145,14 +144,14 @@ class Engine
*/
private function parseInclude($template)
{
- $pattern = '#' . $this->left . 'include +file=[\'"](.*?)[\'"] +/' . $this->right . '#';
+ $pattern = '/' . $this->left . 'include.*?file=[\'"](.*?)[\'"].*?\/' . $this->right . '/is';
$template = preg_replace_callback($pattern, function ($result) {
- $str = null;
+ $string = null;
$file = $this->config['dir'] . $result[1] . '.html';
if (file_exists($file)) {
- $str = file_get_contents($file);
+ $string = file_get_contents($file);
}
- return $str;
+ return $string;
}, $template);
// 处理多层include
if ($this->hasInclude($template)) {
@@ -168,7 +167,7 @@ class Engine
*/
private function hasInclude($template)
{
- $pattern = '#' . $this->left . 'include +file=[\'"](.*?)[\'"] +/' . $this->right . '#';
+ $pattern = '/' . $this->left . 'include.*?file=[\'"](.*?)[\'"].*?\/' . $this->right . '/is';
preg_match($pattern, $template, $matches);
return !empty($matches);
}
@@ -180,7 +179,7 @@ class Engine
*/
private function parseVars($template)
{
- preg_match_all('#{(.*?)}#', $template, $matches);
+ preg_match_all('/{(.*?)}/', $template, $matches);
$search = [];
$replace = [];
for ($i = 0; $i < count($matches[0]); $i++) {
@@ -228,18 +227,19 @@ class Engine
/**
* 获取标签处理结果
- * @param $name
- * @param $tagContent
- * @return mixed
+ * @param $method
+ * @param $tag
+ * @param string $content
+ * @return null
*/
- private function getTagParseResult($name, $tagContent = [])
+ private function getTagParseResult($method, $tag, $content = '')
{
- if (method_exists($this, $name)) {
- return $this->{$name}($tagContent);
+ if (method_exists($this, $method)) {
+ return $this->{$method}($tag, $content);
} else {
foreach ($this->extendInstance as $item) {
- if (method_exists($item, $name)) {
- return $item->{$name}($tagContent);
+ if (method_exists($item, $method)) {
+ return $item->{$method}($tag, $content);
}
}
return null;
@@ -255,31 +255,97 @@ class Engine
private function _parseTags($template, $tags)
{
foreach ($tags as $name => $item) {
- $pattern = '#' . $this->left . $name . '(.*?)' . ($item['close'] ? null : '\/') . $this->right . '#';
- preg_match_all($pattern, $template, $matches);
- for ($i = 0; $i < count($matches[0]); $i++) {
- $tag = [];
- if ($item['attr']) {
- $attrPattern = '#(.*?)=[\'"](.*?)[\'"]#';
- preg_match_all($attrPattern, $matches[1][$i], $result);
- if (isset($result[0]) && !empty($result[0])) {
- foreach ($result[1] as $key => $value) {
- $tag[trim($value, ' ')] = $result[2][$key];
+ $pattern = '/' . $this->left . '(?:(' . $name . ')\b(?>[^' . $this->right . ']*)|\/(' . $name . '))';
+ $pattern .= $this->right . '/is';
+ if ($item['close']) {
+ preg_match_all($pattern, $template, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
+ $nodes = [];
+ if (!empty($matches)) {
+ // 将匹配结果组合为成对数组
+ $start = [];
+ foreach ($matches as $match) {
+ // 为空则为结束标签
+ if ($match[1][0] == '') {
+ $tag = array_pop($start);
+ $nodes[$match[0][1]] = [
+ 'name' => $name,
+ 'start' => $tag[1],
+ 'end' => $match[0][1],
+ 'start_str' => $tag[0],
+ 'end_str' => $match[0][0],
+ ];
+ } else {
+ $start[] = $match[0];
+ }
+ }
+ unset($matches, $start);
+ krsort($nodes);
+
+ 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));
+ while ($startArray) {
+ $begin = end($startArray);
+ // 如果当前结束位置大于最后一个开始标签的位置,则跳过,直接去替换这个结束标签
+ if ($node['end'] > $begin['start']) {
+ break;
+ } else {
+ // 否则先替换掉这个标签后面的所有开始标签
+ $begin = array_pop($startArray);
+ $template = substr_replace($template, $begin['string'], $begin['start'], $begin['length']);
+ }
+ }
+ $template = substr_replace($template, $replace[1], $node['end'], strlen($node['end_str']));
+ $startArray[] = [
+ 'start' => $node['start'],
+ 'length' => strlen($node['start_str']),
+ 'string' => $replace[0]
+ ];
+ }
+ // 替换掉最后入栈,未进入while循环的开始标签
+ while ($startArray) {
+ $begin = array_pop($startArray);
+ $template = substr_replace($template, $begin['string'], $begin['start'], $begin['length']);
}
}
}
- $function = ($item['close']) ? '_' . $name . '_start' : '_' . $name;
- $template = str_replace($matches[0][$i], $this->getTagParseResult($function, $tag), $template);
- }
- if ($item['close']) {
- $closePattern = '#' . $this->left . '\/' . $name . $this->right . '#';
- $template = preg_replace_callback($closePattern, function () use ($name) {
- $function = '_' . $name . '_end';
- return $this->getTagParseResult($function);
+ } 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);
}
}
- return preg_replace('#\?>([\r|\n|\s]*?)<\?php#', '', $template);
+ return preg_replace('/\?>([\r|\n|\s]*?)<\?php/is', '', $template);
+ }
+
+ /**
+ * 获取属性
+ * @param $string
+ * @param array $tags
+ * @return array
+ */
+ private function getAttr($string, $tags = [])
+ {
+ $attr = [];
+ $attrPattern = '/[ +](.*?)=[\'"](.*?)[\'"]/is';
+ preg_match_all($attrPattern, $string, $result);
+ if (isset($result[0]) && !empty($result[0])) {
+ foreach ($result[1] as $key => $value) {
+ $name = trim($value, ' ');
+ if (in_array($name, $tags)) {
+ $attr[$name] = $result[2][$key];
+ }
+ }
+ }
+ return $attr;
}
/**
@@ -289,15 +355,15 @@ class Engine
*/
private function parseRaw($template)
{
- $pattern = '#' . $this->left . 'raw' . $this->right . '([\s\S]*?)';
- $pattern .= $this->left . '\/raw' . $this->right . '#';
+ $pattern = '/' . $this->left . 'raw' . $this->right . '([\s\S]*?)';
+ $pattern .= $this->left . '\/raw' . $this->right . '/is';
$template = preg_replace_callback($pattern, function ($matches) {
return str_replace([
$this->left, $this->right,
'{', '}'
], [
- '',
- '<-raw!--', '--raw->'
+ '',
+ ''
], $matches[1]);
}, $template);
return $template;
@@ -311,8 +377,8 @@ class Engine
public function returnRaw($template)
{
$template = str_replace([
- '',
- '<-raw!--', '--raw->'
+ '',
+ ''
], [
$this->left, $this->right,
'{', '}'
@@ -322,47 +388,35 @@ class Engine
/**
* php标签开始
+ * @param $tag
+ * @param $content
* @return string
*/
- private function _php_start()
+ private function _php($tag, $content)
{
- return '';
+ return '';
}
/**
* if标签
* @param $tag
+ * @param $content
* @return string
*/
- private function _if_start($tag)
+ private function _if($tag, $content)
{
- return '';
+ $parse = '';
+ $parse .= $content;
+ $parse .= '';
+ return $parse;
}
/**
- * if标签结束
- * @return string
- */
- private function _if_end()
- {
- return '';
- }
-
- /**
- * else标签(支持条件)
+ * else标签
* @param $tag
* @return string
*/
- private function _else_start($tag)
+ private function _else($tag)
{
if (isset($tag['condition'])) {
$parse = '';
@@ -375,44 +429,23 @@ class Engine
/**
* volist标签
* @param $tag
+ * @param $content
* @return string
*/
- private function _volist_start($tag)
+ private function _volist($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 = '';
+ $parse = '' : '') . '$' . $tag['id'] . '): ';
+ $parse .= (isset($tag['key']) ? '$' . $tag['key'] . '++;' : '') . ' ?>';
+ $parse .= $content;
+ $parse .= '';
return $parse;
}
- /**
- * volist标签结束
- * @return string
- */
- private function _volist_end()
- {
- return '';
- }
-
- /**
- * assign标签
- * @param $tag
- * @return string
- */
- private function _assign($tag)
- {
- return '';
- }
-
/**
* 获取编译后的内容
* @param $template
- * @return bool|mixed|null|string|string[]
+ * @return mixed|null|string|string[]
*/
public function compile($template)
{
diff --git a/template/Extend.php b/engine/Extend.php
similarity index 91%
rename from template/Extend.php
rename to engine/Extend.php
index 01b2a64..bbff20f 100644
--- a/template/Extend.php
+++ b/engine/Extend.php
@@ -1,6 +1,6 @@
loadTaglib(\template\Extend::class);
+$template->loadTaglib(\engine\Extend::class);
// 编译
$result = $template->compile($content);
// 最后还原raw标签