模板支持点语法、新增empty/notempty/eq/neq标签、支持在自定义标签替换内容中使用模板标签

This commit is contained in:
TOP糯米 2020-07-30 15:01:01 +08:00
parent 4a246c738a
commit 1e47a34920
5 changed files with 392 additions and 36 deletions

View File

@ -4,6 +4,7 @@ namespace top\library;
use Exception;
use top\library\exception\DatabaseException;
use top\library\model\Data;
/**
* 基础模型
@ -11,10 +12,10 @@ use top\library\exception\DatabaseException;
*
* @method $this alias($name)
* @method $this distinct(bool $distinct)
* @method $this field(string|array $field)
* @method $this field(string | array $field)
* @method $this where($field, $condition = null, $value = null)
* @method $this order(string $order)
* @method $this limit(string|array $limit)
* @method $this limit(string | array $limit)
* @method $this join(string $table, string $on, string $type = null)
* @method $this sql()
*/
@ -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);
}
/**

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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');
$search[] = $matches[0][$i];
if ($start == ':') { // 调用函数
if ($start == '$') { // 输出变量
$search[] = $matches[0][$i];
$output = $this->parseParameterOutput($matches[1][$i]);
$replace[] = '<?php echo htmlentities(' . $output . '); ?>';
} elseif ($start == ':') { // 调用函数
$search[] = $matches[0][$i];
$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] . '); ?>';
}
}
$template = str_replace($search, $replace, $template);
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;
}

View File

@ -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;
}
}