mirror of
https://gitee.com/fastadminnet/framework.git
synced 2026-07-01 12:42:48 +08:00
@@ -34,6 +34,9 @@ class Template
|
||||
'compile_type' => 'file', // 模板编译类型
|
||||
'cache_prefix' => '', // 模板缓存前缀标识,可以动态改变
|
||||
'cache_time' => 0, // 模板缓存有效期 0 为永久,(以数字为值,单位:秒)
|
||||
'cache_record_file' => 'cache_record_file', // 记录模板更新时间的文件
|
||||
'layout_on' => false, // 布局模板开关
|
||||
'layout_name' => 'layout', // 布局模板入口文件
|
||||
'layout_item' => '{__CONTENT__}', // 布局模板的内容替换标识
|
||||
'taglib_begin' => '{', // 标签库标签开始标记
|
||||
'taglib_end' => '}', // 标签库标签结束标记
|
||||
@@ -47,8 +50,10 @@ class Template
|
||||
'namespace' => '\\think\\template\\driver\\',
|
||||
];
|
||||
|
||||
private $literal = [];
|
||||
protected $storage = null;
|
||||
private $literal = [];
|
||||
private $includeFile = []; // 记录所有模板包含的文件路径及更新时间
|
||||
private $md5Key = ''; // 保存当前模板的md5码
|
||||
protected $storage = null;
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
@@ -153,7 +158,7 @@ class Template
|
||||
* 渲染模板文件
|
||||
* @access public
|
||||
* @param string $template 模板文件
|
||||
* @param array $vars 模板变量
|
||||
* @param array $vars 模板变量
|
||||
* @param array $config 模板参数
|
||||
* @return void
|
||||
*/
|
||||
@@ -190,7 +195,7 @@ class Template
|
||||
* 渲染模板内容
|
||||
* @access public
|
||||
* @param string $content 模板内容
|
||||
* @param array $vars 模板变量
|
||||
* @param array $vars 模板变量
|
||||
* @return void
|
||||
*/
|
||||
public function fetch($content, $vars = [])
|
||||
@@ -211,7 +216,7 @@ class Template
|
||||
* 检查编译缓存是否有效
|
||||
* 如果无效则需要重新编译
|
||||
* @access private
|
||||
* @param string $template 模板文件名
|
||||
* @param string $template 模板文件名
|
||||
* @param string $cacheFile 缓存文件名
|
||||
* @return boolean
|
||||
*/
|
||||
@@ -221,8 +226,20 @@ class Template
|
||||
// 优先对配置设定检测
|
||||
return false;
|
||||
}
|
||||
// 检查编译存储是否有效
|
||||
return $this->storage->check($template, $cacheFile, $this->config['cache_time']);
|
||||
$this->md5Key = md5($template);
|
||||
$recordFile = $this->config['cache_path'] . md5($this->config['cache_record_file']) . $this->config['cache_suffix'];
|
||||
if (!is_file($recordFile)) {
|
||||
return false;
|
||||
} else {
|
||||
$this->includeFile = $this->storage->read($recordFile, [], true);
|
||||
if (!isset($this->includeFile[$this->md5Key])) {
|
||||
// 缓存记录中无此模板
|
||||
return false;
|
||||
} else {
|
||||
// 检查编译存储是否有效
|
||||
return $this->storage->check($this->includeFile[$this->md5Key], $cacheFile, $this->config['cache_time']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,6 +266,26 @@ class Template
|
||||
*/
|
||||
private function compiler(&$content, $cacheFile)
|
||||
{
|
||||
// 判断是否启用布局
|
||||
if ($this->config['layout_on']) {
|
||||
if (false !== strpos($content, '{__NOLAYOUT__}')) {
|
||||
// 可以单独定义不使用布局
|
||||
$content = str_replace('{__NOLAYOUT__}', '', $content);
|
||||
} else {
|
||||
// 读取布局模板
|
||||
$layoutFile = $this->parseTemplateFile($this->config['layout_name']);
|
||||
// 检查布局文件
|
||||
if (is_file($layoutFile)) {
|
||||
// 替换布局的主体内容
|
||||
$content = str_replace($this->config['layout_item'], $content, file_get_contents($layoutFile));
|
||||
// 记录布局文件的更新时间
|
||||
$this->includeFile[$this->md5Key][filemtime($layoutFile)] = $layoutFile;
|
||||
} else {
|
||||
throw new Exception('template not exist:' . $layoutFile, 10700);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 模板解析
|
||||
$this->parse($content);
|
||||
// 添加安全代码
|
||||
@@ -266,6 +303,10 @@ class Template
|
||||
$content = str_replace(array_keys($replace), array_values($replace), $content);
|
||||
// 编译存储
|
||||
$this->storage->write($cacheFile, $content);
|
||||
// 保存模板更新记录
|
||||
$string = "<?php\nreturn " . var_export($this->includeFile, true) . ';';
|
||||
$this->storage->write($this->config['cache_path'] . md5($this->config['cache_record_file']) . $this->config['cache_suffix'], $string);
|
||||
$this->includeFile = [];
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -361,15 +402,17 @@ class Template
|
||||
$content = str_replace($matches[0], '', $content);
|
||||
// 解析Layout标签
|
||||
$array = $this->parseAttr($matches[0]);
|
||||
//if (!C('LAYOUT_ON') || C('LAYOUT_NAME') != $array['name']) {
|
||||
// 读取布局模板
|
||||
$layoutFile = (defined('THEME_PATH') && substr_count($array['name'], '/') < 2 ? THEME_PATH : $this->config['view_path']) . $array['name'] . $this->config['view_suffix'];
|
||||
if (is_file($layoutFile)) {
|
||||
$replace = isset($array['replace']) ? $array['replace'] : $this->config['layout_item'];
|
||||
// 替换布局的主体内容
|
||||
$content = str_replace($replace, $content, file_get_contents($layoutFile));
|
||||
if (!$this->config['layout_on'] || $this->config['layout_name'] != $array['name']) {
|
||||
// 读取布局模板
|
||||
$layoutFile = $this->parseTemplateFile($array['name']);
|
||||
if (is_file($layoutFile)) {
|
||||
$replace = isset($array['replace']) ? $array['replace'] : $this->config['layout_item'];
|
||||
// 替换布局的主体内容
|
||||
$content = str_replace($replace, $content, file_get_contents($layoutFile));
|
||||
// 记录布局文件的更新时间
|
||||
$this->includeFile[$this->md5Key][filemtime($layoutFile)] = $layoutFile;
|
||||
}
|
||||
}
|
||||
//}
|
||||
} else {
|
||||
$content = str_replace('{__NOLAYOUT__}', '', $content);
|
||||
}
|
||||
@@ -397,7 +440,7 @@ class Template
|
||||
foreach ($array as $k => $v) {
|
||||
// 以$开头字符串转换成模板变量
|
||||
if (0 === strpos($v, '$')) {
|
||||
$v = ltrim($this->config['tpl_begin'], '\\') . $v . ltrim($this->config['tpl_end'], '\\');
|
||||
$v = $this->get(substr($v, 1));
|
||||
}
|
||||
$parseStr = str_replace('[' . $k . ']', $v, $parseStr);
|
||||
}
|
||||
@@ -422,7 +465,7 @@ class Template
|
||||
private function parseExtend(&$content)
|
||||
{
|
||||
$regex = $this->getRegex('extend');
|
||||
$array = $blocks = $extBlocks = [];
|
||||
$array = $blocks = $extBlocks = [];
|
||||
$extend = '';
|
||||
$fun = function ($template) use (&$fun, &$regex, &$array, &$extend, &$blocks, &$extBlocks) {
|
||||
if (preg_match($regex, $template, $matches)) {
|
||||
@@ -515,7 +558,7 @@ class Template
|
||||
'content' => substr($content, $start, $len),
|
||||
'end' => $end,
|
||||
];
|
||||
$keys[] = $begin['offset'];
|
||||
$keys[] = $begin['offset'];
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
@@ -920,6 +963,8 @@ class Template
|
||||
if (is_file($template)) {
|
||||
// 获取模板文件内容
|
||||
$parseStr .= file_get_contents($template);
|
||||
// 记录模板文件的更新时间
|
||||
$this->includeFile[$this->md5Key][filemtime($template)] = $template;
|
||||
}
|
||||
}
|
||||
return $parseStr;
|
||||
|
||||
@@ -15,7 +15,12 @@ use think\Exception;
|
||||
|
||||
class File
|
||||
{
|
||||
// 写入编译缓存
|
||||
/**
|
||||
* 写入编译缓存
|
||||
* @string $cacheFile 缓存的文件名
|
||||
* @string $content 缓存的内容
|
||||
* @return void|array
|
||||
*/
|
||||
public function write($cacheFile, $content)
|
||||
{
|
||||
// 检测模板目录
|
||||
@@ -29,22 +34,43 @@ class File
|
||||
}
|
||||
}
|
||||
|
||||
// 读取编译编译
|
||||
public function read($cacheFile, $vars)
|
||||
/**
|
||||
* 读取编译编译
|
||||
* @string $cacheFile 缓存的文件名
|
||||
* @array $vars 变量数组
|
||||
* @boolean $isReturn 是否返回内容
|
||||
* @return void|array
|
||||
*/
|
||||
public function read($cacheFile, $vars = [], $isReturn = false)
|
||||
{
|
||||
// 模板阵列变量分解成为独立变量
|
||||
extract($vars, EXTR_OVERWRITE);
|
||||
//载入模版缓存文件
|
||||
include $cacheFile;
|
||||
if (!empty($vars) && is_array($vars)) {
|
||||
// 模板阵列变量分解成为独立变量
|
||||
extract($vars, EXTR_OVERWRITE);
|
||||
}
|
||||
if ($isReturn) {
|
||||
//载入模版缓存文件
|
||||
include $cacheFile;
|
||||
} else {
|
||||
return include $cacheFile;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查编译缓存是否有效
|
||||
public function check($template, $cacheFile, $cacheTime)
|
||||
/**
|
||||
* 检查编译缓存是否有效
|
||||
* @array $template 用到的模板更新时间列表
|
||||
* @string $cacheFile 缓存的文件名
|
||||
* @int $cacheTime 缓存时间
|
||||
* @return boolean
|
||||
*/
|
||||
public function check($files, $cacheFile, $cacheTime)
|
||||
{
|
||||
if (!is_file($cacheFile) || (is_file($template) && filemtime($template) > filemtime($cacheFile))) {
|
||||
// 模板文件如果有更新则缓存需要更新
|
||||
return false;
|
||||
} elseif (0 != $cacheTime && time() > filemtime($cacheFile) + $cacheTime) {
|
||||
foreach($files as $time => $path) {
|
||||
if (is_file($path) && filemtime($path) > $time) {
|
||||
// 模板文件如果有更新则缓存需要更新
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (0 != $cacheTime && time() > filemtime($cacheFile) + $cacheTime) {
|
||||
// 缓存是否在有效期
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,12 @@ class Sae
|
||||
}
|
||||
}
|
||||
|
||||
// 写入编译缓存
|
||||
/**
|
||||
* 写入编译缓存
|
||||
* @string $cacheFile 缓存的文件名
|
||||
* @string $content 缓存的内容
|
||||
* @return void|array
|
||||
*/
|
||||
public function write($cacheFile, $content)
|
||||
{
|
||||
// 添加写入时间
|
||||
@@ -48,23 +53,42 @@ class Sae
|
||||
}
|
||||
}
|
||||
|
||||
// 读取编译编译
|
||||
public function read($cacheFile, $vars)
|
||||
/**
|
||||
* 读取编译编译
|
||||
* @string $cacheFile 缓存的文件名
|
||||
* @array $vars 变量数组
|
||||
* @boolean $isReturn 是否返回内容
|
||||
* @return void|array
|
||||
*/
|
||||
public function read($cacheFile, $vars = [], $isReturn = false)
|
||||
{
|
||||
if (!is_null($vars)) {
|
||||
if (!empty($vars) && is_array($vars)) {
|
||||
extract($vars, EXTR_OVERWRITE);
|
||||
}
|
||||
eval('?>' . $this->get($cacheFile, 'content'));
|
||||
if ($isReturn) {
|
||||
return $this->get($cacheFile, 'content');
|
||||
} else {
|
||||
eval('?>' . $this->get($cacheFile, 'content'));
|
||||
}
|
||||
}
|
||||
|
||||
// 检查编译缓存是否有效
|
||||
/**
|
||||
* 检查编译缓存是否有效
|
||||
* @array $template 用到的模板更新时间列表
|
||||
* @string $cacheFile 缓存的文件名
|
||||
* @int $cacheTime 缓存时间
|
||||
* @return boolean
|
||||
*/
|
||||
public function check($template, $cacheFile, $cacheTime)
|
||||
{
|
||||
foreach($template as $time => $path) {
|
||||
if (is_file($path) && filemtime($path) > $time) {
|
||||
// 模板文件如果有更新则缓存需要更新
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$mtime = $this->get($cacheFile, 'mtime');
|
||||
if (!$this->get($cacheFile, 'content') || (is_file($template) && filemtime($template) > $mtime)) {
|
||||
// 模板文件如果有更新则缓存需要更新
|
||||
return false;
|
||||
}if (0 != $cacheTime && time() > $mtime + $cacheTime) {
|
||||
if (0 != $cacheTime && time() > $mtime + $cacheTime) {
|
||||
// 缓存是否在有效期
|
||||
return false;
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{layout name="layout" replace="[__REPLACE__]" /}
|
||||
{layout name="layout2" replace="[__REPLACE__]" /}
|
||||
<div>
|
||||
{include file="include" name="info" value="$info.value" /}
|
||||
{block name="main"}
|
||||
|
||||
@@ -1 +1 @@
|
||||
include2
|
||||
{$info.value}:
|
||||
@@ -1,2 +1,2 @@
|
||||
<nav>[__REPLACE__]
|
||||
</nav>
|
||||
<div>{__CONTENT__}
|
||||
</div>
|
||||
2
tests/thinkphp/library/think/layout2.html
Normal file
2
tests/thinkphp/library/think/layout2.html
Normal file
@@ -0,0 +1,2 @@
|
||||
<nav>[__REPLACE__]
|
||||
</nav>
|
||||
@@ -27,7 +27,7 @@ class templateTest extends \PHPUnit_Framework_TestCase
|
||||
$content = <<<EOF
|
||||
{\$name.a.b}
|
||||
EOF;
|
||||
$data = <<<EOF
|
||||
$data = <<<EOF
|
||||
<?php echo \$name['a']['b']; ?>
|
||||
EOF;
|
||||
|
||||
@@ -37,7 +37,7 @@ EOF;
|
||||
$content = <<<EOF
|
||||
{\$name.a??'test'}
|
||||
EOF;
|
||||
$data = <<<EOF
|
||||
$data = <<<EOF
|
||||
<?php echo isset(\$name['a']) ? \$name['a'] : 'test'; ?>
|
||||
EOF;
|
||||
|
||||
@@ -47,7 +47,7 @@ EOF;
|
||||
$content = <<<EOF
|
||||
{\$name.a?='test'}
|
||||
EOF;
|
||||
$data = <<<EOF
|
||||
$data = <<<EOF
|
||||
<?php if(!empty(\$name['a'])) echo 'test'; ?>
|
||||
EOF;
|
||||
|
||||
@@ -57,7 +57,7 @@ EOF;
|
||||
$content = <<<EOF
|
||||
{\$name.a?:'test'}
|
||||
EOF;
|
||||
$data = <<<EOF
|
||||
$data = <<<EOF
|
||||
<?php echo !empty(\$name['a'])?\$name['a']:'test'; ?>
|
||||
EOF;
|
||||
|
||||
@@ -67,7 +67,7 @@ EOF;
|
||||
$content = <<<EOF
|
||||
{\$name.a?\$name.b:'no'}
|
||||
EOF;
|
||||
$data = <<<EOF
|
||||
$data = <<<EOF
|
||||
<?php echo !empty(\$name['a'])?\$name['b']:'no'; ?>
|
||||
EOF;
|
||||
|
||||
@@ -77,7 +77,7 @@ EOF;
|
||||
$content = <<<EOF
|
||||
{\$name.a==\$name.b?='test'}
|
||||
EOF;
|
||||
$data = <<<EOF
|
||||
$data = <<<EOF
|
||||
<?php if(\$name['a']==\$name['b']) echo 'test'; ?>
|
||||
EOF;
|
||||
|
||||
@@ -87,7 +87,7 @@ EOF;
|
||||
$content = <<<EOF
|
||||
{\$name.a==\$name.b?'a':'b'}
|
||||
EOF;
|
||||
$data = <<<EOF
|
||||
$data = <<<EOF
|
||||
<?php echo (\$name['a']==\$name['b'])?'a':'b'; ?>
|
||||
EOF;
|
||||
|
||||
@@ -97,7 +97,7 @@ EOF;
|
||||
$content = <<<EOF
|
||||
{\$name.a|default='test'==\$name.b?'a':'b'}
|
||||
EOF;
|
||||
$data = <<<EOF
|
||||
$data = <<<EOF
|
||||
<?php echo ((isset(\$name['a']) && (\$name['a'] !== '')?\$name['a']:'test')==\$name['b'])?'a':'b'; ?>
|
||||
EOF;
|
||||
|
||||
@@ -107,7 +107,7 @@ EOF;
|
||||
$content = <<<EOF
|
||||
{\$name.a|trim==\$name.b?='eq'}
|
||||
EOF;
|
||||
$data = <<<EOF
|
||||
$data = <<<EOF
|
||||
<?php if(trim(\$name['a'])==\$name['b']) echo 'eq'; ?>
|
||||
EOF;
|
||||
|
||||
@@ -117,7 +117,7 @@ EOF;
|
||||
$content = <<<EOF
|
||||
{:ltrim(rtrim(\$name.a))}
|
||||
EOF;
|
||||
$data = <<<EOF
|
||||
$data = <<<EOF
|
||||
<?php echo ltrim(rtrim(\$name['a'])); ?>
|
||||
EOF;
|
||||
|
||||
@@ -127,7 +127,7 @@ EOF;
|
||||
$content = <<<EOF
|
||||
{~echo(trim(\$name.a))}
|
||||
EOF;
|
||||
$data = <<<EOF
|
||||
$data = <<<EOF
|
||||
<?php echo(trim(\$name['a'])); ?>
|
||||
EOF;
|
||||
|
||||
@@ -137,7 +137,7 @@ EOF;
|
||||
$content = <<<EOF
|
||||
{++\$name.a}
|
||||
EOF;
|
||||
$data = <<<EOF
|
||||
$data = <<<EOF
|
||||
<?php echo ++\$name['a']; ?>
|
||||
EOF;
|
||||
|
||||
@@ -147,7 +147,7 @@ EOF;
|
||||
$content = <<<EOF
|
||||
{/*\$name*/}
|
||||
EOF;
|
||||
$data = '';
|
||||
$data = '';
|
||||
|
||||
$template->parse($content);
|
||||
$this->assertEquals($data, $content);
|
||||
@@ -155,7 +155,7 @@ EOF;
|
||||
$content = <<<EOF
|
||||
{\$0a}
|
||||
EOF;
|
||||
$data = '{$0a}';
|
||||
$data = '{$0a}';
|
||||
|
||||
$template->parse($content);
|
||||
$this->assertEquals($data, $content);
|
||||
@@ -240,54 +240,13 @@ EOF;
|
||||
$content = <<<EOF
|
||||
{\$info2.b|trim?'yes':'no'}
|
||||
EOF;
|
||||
$data = <<<EOF
|
||||
$data = <<<EOF
|
||||
<?php echo trim(\$info2->b)?'yes':'no'; ?>
|
||||
EOF;
|
||||
$template2->parse($content);
|
||||
$this->assertEquals($data, $content);
|
||||
}
|
||||
|
||||
public function testTag()
|
||||
{
|
||||
$config['view_path'] = dirname(__FILE__) . '/';
|
||||
$config['view_suffix'] = '.html';
|
||||
$template = new Template($config);
|
||||
$files = ['extend' => 'extend', 'include' => 'include'];
|
||||
$template->assign('files', $files);
|
||||
|
||||
$content = <<<EOF
|
||||
{extend name="\$files.extend" /}
|
||||
{block name="side"}
|
||||
{include file="\$files.include" name="\$user.name" value="\$user.account" /}
|
||||
{\$message}{literal}{\$message}{/literal}
|
||||
{/block}
|
||||
EOF;
|
||||
$data = <<<EOF
|
||||
<nav>
|
||||
<div>
|
||||
<input name="info" value="<?php echo \$info['value']; ?>">
|
||||
include2
|
||||
|
||||
|
||||
<input name="<?php echo \$user['name']; ?>" value="<?php echo \$user['account']; ?>">
|
||||
include2
|
||||
<?php echo \$message; ?>{\$message}
|
||||
|
||||
|
||||
mainbody
|
||||
|
||||
|
||||
|
||||
{\$name}
|
||||
|
||||
<?php echo 'php code'; ?>
|
||||
</div>
|
||||
</nav>
|
||||
EOF;
|
||||
$template->parse($content);
|
||||
$this->assertEquals($data, $content);
|
||||
}
|
||||
|
||||
public function testThinkVar()
|
||||
{
|
||||
$config['tpl_begin'] = '{';
|
||||
@@ -321,7 +280,7 @@ EOF;
|
||||
{\$Think.SITE_NAME}<br/>
|
||||
{\$Think.SITE.URL}
|
||||
EOF;
|
||||
$data = <<<EOF
|
||||
$data = <<<EOF
|
||||
<?php echo \$_SERVER['SERVER_NAME']; ?><br/>
|
||||
<?php echo \$_GET['action']; ?><br/>
|
||||
<?php echo \$_POST['action']; ?><br/>
|
||||
@@ -353,22 +312,54 @@ EOF;
|
||||
'strip_space' => true,
|
||||
'view_path' => dirname(__FILE__) . '/',
|
||||
];
|
||||
$data = ['name' => 'value'];
|
||||
$data = ['name' => 'value'];
|
||||
$template->display('display', $data, $config);
|
||||
$this->expectOutputString('value');
|
||||
}
|
||||
|
||||
public function testFetch()
|
||||
{
|
||||
$template = new Template();
|
||||
$template->view_path = dirname(__FILE__) . '/';
|
||||
$data = ['name' => 'value'];
|
||||
$content = <<<EOF
|
||||
{\$name}
|
||||
EOF;
|
||||
$config['view_path'] = dirname(__FILE__) . '/';
|
||||
$config['view_suffix'] = '.html';
|
||||
$config['layout_on'] = true;
|
||||
$config['layout_name'] = 'layout';
|
||||
$template = new Template($config);
|
||||
$files = ['extend' => 'extend', 'include' => 'include'];
|
||||
$template->assign('files', $files);
|
||||
$template->assign('user', ['name' => 'name', 'account' => 100]);
|
||||
$template->assign('message', 'message');
|
||||
$template->assign('info', ['value' => 'value']);
|
||||
|
||||
$template->fetch($content, $data);
|
||||
$this->expectOutputString('value');
|
||||
$content = <<<EOF
|
||||
{extend name="\$files.extend" /}
|
||||
{block name="side"}
|
||||
{include file="\$files.include" name="\$user.name" value="\$user.account" /}
|
||||
{\$message}{literal}{\$message}{/literal}
|
||||
{/block}
|
||||
EOF;
|
||||
$content2 = <<<EOF
|
||||
<nav>
|
||||
<div>
|
||||
<input name="info" value="value">
|
||||
value:
|
||||
|
||||
|
||||
<input name="name" value="100">
|
||||
value:
|
||||
message{\$message}
|
||||
|
||||
|
||||
mainbody
|
||||
|
||||
|
||||
|
||||
{\$name}
|
||||
|
||||
php code</div>
|
||||
</nav>
|
||||
EOF;
|
||||
$template->fetch($content);
|
||||
$this->expectOutputString($content2);
|
||||
}
|
||||
|
||||
public function testVarAssign()
|
||||
|
||||
Reference in New Issue
Block a user