模板支持点语法、新增empty/notempty/eq/neq标签、支持在自定义标签替换内容中使用模板标签
This commit is contained in:
parent
4a246c738a
commit
1e47a34920
|
@ -4,6 +4,7 @@ namespace top\library;
|
|||
|
||||
use Exception;
|
||||
use top\library\exception\DatabaseException;
|
||||
use top\library\model\Data;
|
||||
|
||||
/**
|
||||
* 基础模型
|
||||
|
@ -83,6 +84,12 @@ class Model
|
|||
*/
|
||||
private $isInsert = false;
|
||||
|
||||
/**
|
||||
* 当前查询到的数据
|
||||
* @var array
|
||||
*/
|
||||
private $data = [];
|
||||
|
||||
/**
|
||||
* 用数据库配置获取实例
|
||||
* Model constructor.
|
||||
|
@ -250,9 +257,9 @@ class Model
|
|||
|
||||
/**
|
||||
* 查询单条记录
|
||||
* @param string|bool $param
|
||||
* @param bool $param
|
||||
* @param bool $notRaw
|
||||
* @return array
|
||||
* @return Data
|
||||
*/
|
||||
public function find($param = false, $notRaw = true)
|
||||
{
|
||||
|
@ -262,14 +269,14 @@ class Model
|
|||
$result = $this->outHandle($result);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
return new Data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有记录
|
||||
* @param string|bool $param
|
||||
* @param bool $param
|
||||
* @param bool $notRaw
|
||||
* @return array
|
||||
* @return Data
|
||||
*/
|
||||
public function select($param = false, $notRaw = true)
|
||||
{
|
||||
|
@ -279,7 +286,7 @@ class Model
|
|||
$result = $this->outHandle($result);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
return new Data($result);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
<?php
|
||||
/**
|
||||
* Author: TopNuoMi
|
||||
* Date: 2020/07/07
|
||||
*/
|
||||
|
||||
namespace top\library\model;
|
||||
|
||||
use ArrayAccess;
|
||||
use Iterator;
|
||||
|
||||
class Data implements ArrayAccess, Iterator
|
||||
{
|
||||
private $data = [];
|
||||
|
||||
public function __construct($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取$this->>data中的值
|
||||
* @param $key
|
||||
* @return mixed|null
|
||||
*/
|
||||
private function getValue($key)
|
||||
{
|
||||
if (isset($this->data[$key]) && array_key_exists($key, $this->data)) {
|
||||
return $this->data[$key];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 外部获取$this->data中的值
|
||||
* @param $key
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function value($key)
|
||||
{
|
||||
return $this->getValue($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字段当做属性调用
|
||||
* @param $name
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
return $this->getValue($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a offset exists
|
||||
* @link https://php.net/manual/en/arrayaccess.offsetexists.php
|
||||
* @param mixed $offset <p>
|
||||
* An offset to check for.
|
||||
* </p>
|
||||
* @return boolean true on success or false on failure.
|
||||
* </p>
|
||||
* <p>
|
||||
* The return value will be casted to boolean if non-boolean was returned.
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return false !== $this->getValue($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to retrieve
|
||||
* @link https://php.net/manual/en/arrayaccess.offsetget.php
|
||||
* @param mixed $offset <p>
|
||||
* The offset to retrieve.
|
||||
* </p>
|
||||
* @return mixed Can return all value types.
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
if ($this->offsetExists($offset)) {
|
||||
return $this->getValue($offset);
|
||||
}
|
||||
trigger_error('不存在的数组索引:' . $offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to set
|
||||
* @link https://php.net/manual/en/arrayaccess.offsetset.php
|
||||
* @param mixed $offset <p>
|
||||
* The offset to assign the value to.
|
||||
* </p>
|
||||
* @param mixed $value <p>
|
||||
* The value to set.
|
||||
* </p>
|
||||
* @return void
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->data[$offset] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to unset
|
||||
* @link https://php.net/manual/en/arrayaccess.offsetunset.php
|
||||
* @param mixed $offset <p>
|
||||
* The offset to unset.
|
||||
* </p>
|
||||
* @return void
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->data[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current element
|
||||
* @link https://php.net/manual/en/iterator.current.php
|
||||
* @return mixed Can return any type.
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return current($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move forward to next element
|
||||
* @link https://php.net/manual/en/iterator.next.php
|
||||
* @return void Any returned value is ignored.
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
return next($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key of the current element
|
||||
* @link https://php.net/manual/en/iterator.key.php
|
||||
* @return mixed scalar on success, or null on failure.
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return key($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current position is valid
|
||||
* @link https://php.net/manual/en/iterator.valid.php
|
||||
* @return boolean The return value will be casted to boolean and then evaluated.
|
||||
* Returns true on success or false on failure.
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return $this->current() !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind the Iterator to the first element
|
||||
* @link https://php.net/manual/en/iterator.rewind.php
|
||||
* @return void Any returned value is ignored.
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
reset($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转数组
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转JSON
|
||||
* @return string
|
||||
*/
|
||||
public function toJson()
|
||||
{
|
||||
return json_encode($this->data, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
/**
|
||||
* Author: TopNuoMi
|
||||
* Date: 2020/07/09
|
||||
*/
|
||||
|
||||
namespace top\library\template;
|
||||
|
||||
abstract class TagLib
|
||||
{
|
||||
public $tags = [];
|
||||
|
||||
/**
|
||||
* 处理点语法
|
||||
* @param $string
|
||||
* @return null|string|string[]
|
||||
*/
|
||||
protected final function parseDotSyntax($string)
|
||||
{
|
||||
// 处理.语法(仅数组或已实现数组访问接口的对象)
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
|
@ -15,6 +15,11 @@ class Engine
|
|||
|
||||
use Instance;
|
||||
|
||||
/**
|
||||
* @var array 标签定义
|
||||
*/
|
||||
protected $tags = [];
|
||||
|
||||
/**
|
||||
* @var string 左定界符
|
||||
*/
|
||||
|
@ -25,15 +30,10 @@ class Engine
|
|||
*/
|
||||
private $right = '>';
|
||||
|
||||
/**
|
||||
* @var array 标签定义
|
||||
*/
|
||||
protected $tags = [];
|
||||
|
||||
/**
|
||||
* @var array 模板配置
|
||||
*/
|
||||
protected $config = [];
|
||||
private $config = [];
|
||||
|
||||
/**
|
||||
* @var array 标签库
|
||||
|
@ -80,8 +80,8 @@ class Engine
|
|||
$extendFileContent = file_get_contents($file);
|
||||
}
|
||||
// 处理继承中的include标签
|
||||
$tempContent = $this->parseInclude($extendFileContent);
|
||||
$extendFileContent = $tempContent !== false ? $tempContent : $extendFileContent;
|
||||
// $tempContent = $this->parseInclude($extendFileContent);
|
||||
// $extendFileContent = $tempContent !== false ? $tempContent : $extendFileContent;
|
||||
// 被继承模板中的块
|
||||
preg_match_all($blockPattern, $extendFileContent, $extendResult);
|
||||
// 继承模板中的块
|
||||
|
@ -140,13 +140,12 @@ class Engine
|
|||
/**
|
||||
* 检测是否含有include
|
||||
* @param $template
|
||||
* @return bool
|
||||
* @return int|false
|
||||
*/
|
||||
private function hasInclude($template)
|
||||
{
|
||||
$pattern = '/' . $this->left . 'include\s+file[\s\S]*?=[\s\S]*?[\'"](.*?)[\'"][\s\S]*?\/' . $this->right . '/is';
|
||||
preg_match($pattern, $template, $matches);
|
||||
return !empty($matches);
|
||||
return preg_match($pattern, $template, $matches);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -162,21 +161,83 @@ class Engine
|
|||
for ($i = 0; $i < count($matches[0]); $i++) {
|
||||
$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[] = '<?php echo htmlentities(' . $output . '); ?>';
|
||||
} elseif ($start == ':') { // 调用函数
|
||||
$search[] = $matches[0][$i];
|
||||
if ($start == ':') { // 调用函数
|
||||
$replace[] = '<?php echo (' . ltrim($matches[1][$i], ':') . '); ?>';
|
||||
} elseif ($start == '@') { // 输出常量
|
||||
$replace[] = '<?php echo (' . ltrim($matches[1][$i], '@') . '); ?>';
|
||||
$search[] = $matches[0][$i];
|
||||
$replace[] = '<?php echo htmlentities(' . ltrim($matches[1][$i], '@') . '); ?>';
|
||||
} elseif ($start == '*' && $end == '*') { // 注释
|
||||
$search[] = $matches[0][$i];
|
||||
$replace[] = '<?php /* ' . trim($matches[1][$i], '*') . ' */ ?>';
|
||||
} else { // 输出变量
|
||||
$replace[] = '<?php echo (' . $matches[1][$i] . '); ?>';
|
||||
}
|
||||
}
|
||||
if (!empty($search) && !empty($replace)) {
|
||||
$template = str_replace($search, $replace, $template);
|
||||
}
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析变量输出
|
||||
* @param $output
|
||||
* @return string
|
||||
*/
|
||||
private function parseParameterOutput($output)
|
||||
{
|
||||
// 处理|函数调用
|
||||
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 $string
|
||||
* @return null|string|string[]
|
||||
*/
|
||||
private function parseDotSyntax($string)
|
||||
{
|
||||
// 处理.语法(仅数组或已实现数组访问接口的对象)
|
||||
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
|
||||
|
@ -225,8 +286,7 @@ class Engine
|
|||
if (method_exists($this->libInstance[$tagInfo[0]], '_' . $tagInfo[1])) {
|
||||
return $this->libInstance[$tagInfo[0]]->{'_' . $tagInfo[1]}($attr, $content);
|
||||
}
|
||||
}
|
||||
// 否则尝试默认标签处理
|
||||
} // 否则尝试默认标签处理
|
||||
else if (method_exists($this->libInstance['default'], '_' . $name)) {
|
||||
return $this->libInstance['default']->{'_' . $name}($attr, $content);
|
||||
}
|
||||
|
@ -235,7 +295,6 @@ class Engine
|
|||
/**
|
||||
* 进行标签处理
|
||||
* @param $template
|
||||
* @param $tags
|
||||
* @return null|string|string[]
|
||||
*/
|
||||
private function parseTags($template)
|
||||
|
@ -276,8 +335,9 @@ class Engine
|
|||
// 得到准备替换的值
|
||||
$replace = explode($cut, $this->getTagParseResult($name, $attr, $cut));
|
||||
$replace = [
|
||||
(isset($replace[0])) ? $replace[0] : [],
|
||||
(isset($replace[1])) ? $replace[1] : [],
|
||||
// 递归解析标签,使之可以在自定义标签中嵌套标签
|
||||
(isset($replace[0])) ? $this->parseTags($replace[0]) : '',
|
||||
(isset($replace[1])) ? $replace[1] : '',
|
||||
];
|
||||
while ($startArray) {
|
||||
$begin = end($startArray);
|
||||
|
@ -314,7 +374,7 @@ class Engine
|
|||
}, $template);
|
||||
}
|
||||
}
|
||||
return preg_replace('/\?>([\r|\n|\s]*?)<\?php/is', '', $template);
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -364,7 +424,7 @@ class Engine
|
|||
* @param $template
|
||||
* @return mixed
|
||||
*/
|
||||
public function returnOriginal($template)
|
||||
private function returnOriginal($template)
|
||||
{
|
||||
return str_replace([
|
||||
'<!ORIGINAL--', '--ORIGINAL>',
|
||||
|
@ -388,12 +448,14 @@ class Engine
|
|||
$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 '<?php if (!defined(\'APP_PATH\')) exit; ?>' . $template;
|
||||
}
|
||||
|
|
|
@ -22,9 +22,12 @@ class Tags
|
|||
'assign' => ['attr' => 'name,value', 'close' => 0],
|
||||
'switch' => ['attr' => 'name', 'close' => 1],
|
||||
'case' => ['attr' => 'value', 'close' => 1],
|
||||
'empty' => ['attr' => 'name', 'close' => 1],
|
||||
'notempty' => ['attr' => 'name', 'close' => 1],
|
||||
'eq' => ['attr' => 'name,value', 'close' => 1],
|
||||
'neq' => ['attr' => 'name,value', 'close' => 1],
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* php标签
|
||||
* @param $attr
|
||||
|
@ -162,4 +165,65 @@ class Tags
|
|||
}
|
||||
return $parse;
|
||||
}
|
||||
|
||||
/**
|
||||
* empty标签
|
||||
* @param $attr
|
||||
* @param $content
|
||||
* @return string
|
||||
*/
|
||||
public function _empty($attr, $content)
|
||||
{
|
||||
$parse = "<?php if (empty(\${$attr['name']})): ?>";
|
||||
$parse .= $content;
|
||||
$parse .= '<?php endif; ?>';
|
||||
return $parse;
|
||||
}
|
||||
|
||||
/**
|
||||
* notempty标签
|
||||
* @param $attr
|
||||
* @param $content
|
||||
* @return string
|
||||
*/
|
||||
public function _notempty($attr, $content)
|
||||
{
|
||||
$parse = "<?php if (!empty(\${$attr['name']})): ?>";
|
||||
$parse .= $content;
|
||||
$parse .= '<?php endif; ?>';
|
||||
return $parse;
|
||||
}
|
||||
|
||||
/**
|
||||
* eq标签
|
||||
* @param $attr
|
||||
* @param $content
|
||||
* @return string
|
||||
*/
|
||||
public function _eq($attr, $content)
|
||||
{
|
||||
$name = is_numeric($attr['name']) ? $attr['name'] : "\$'{$attr['name']}'";
|
||||
$value = is_numeric($attr['value']) ? $attr['value'] : "\$'{$attr['value']}'";
|
||||
$parse = "<?php if ({$name} == {$value}): ?>";
|
||||
$parse .= '{' . $content . '}';
|
||||
$parse .= '<?php endif; ?>';
|
||||
return $parse;
|
||||
}
|
||||
|
||||
/**
|
||||
* neq标签
|
||||
* @param $attr
|
||||
* @param $content
|
||||
* @return string
|
||||
*/
|
||||
public function _neq($attr, $content)
|
||||
{
|
||||
$name = is_numeric($attr['name']) ? $attr['name'] : "\$'{$attr['name']}'";
|
||||
$value = is_numeric($attr['value']) ? $attr['value'] : "\$'{$attr['value']}'";
|
||||
$parse = "<?php if ({$name} != {$value}): ?>";
|
||||
$parse .= '{' . $content . '}';
|
||||
$parse .= '<?php endif; ?>';
|
||||
return $parse;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue