This commit is contained in:
thinkphp
2016-02-19 11:35:55 +08:00
8 changed files with 199 additions and 111 deletions

View File

@@ -34,6 +34,9 @@ class Template
'compile_type' => 'file', // 模板编译类型 'compile_type' => 'file', // 模板编译类型
'cache_prefix' => '', // 模板缓存前缀标识,可以动态改变 'cache_prefix' => '', // 模板缓存前缀标识,可以动态改变
'cache_time' => 0, // 模板缓存有效期 0 为永久,(以数字为值,单位:秒) 'cache_time' => 0, // 模板缓存有效期 0 为永久,(以数字为值,单位:秒)
'cache_record_file' => 'cache_record_file', // 记录模板更新时间的文件
'layout_on' => false, // 布局模板开关
'layout_name' => 'layout', // 布局模板入口文件
'layout_item' => '{__CONTENT__}', // 布局模板的内容替换标识 'layout_item' => '{__CONTENT__}', // 布局模板的内容替换标识
'taglib_begin' => '{', // 标签库标签开始标记 'taglib_begin' => '{', // 标签库标签开始标记
'taglib_end' => '}', // 标签库标签结束标记 'taglib_end' => '}', // 标签库标签结束标记
@@ -48,6 +51,8 @@ class Template
]; ];
private $literal = []; private $literal = [];
private $includeFile = []; // 记录所有模板包含的文件路径及更新时间
private $md5Key = ''; // 保存当前模板的md5码
protected $storage = null; protected $storage = null;
/** /**
@@ -221,8 +226,20 @@ class Template
// 优先对配置设定检测 // 优先对配置设定检测
return false; return false;
} }
$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($template, $cacheFile, $this->config['cache_time']); return $this->storage->check($this->includeFile[$this->md5Key], $cacheFile, $this->config['cache_time']);
}
}
} }
/** /**
@@ -249,6 +266,26 @@ class Template
*/ */
private function compiler(&$content, $cacheFile) 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); $this->parse($content);
// 添加安全代码 // 添加安全代码
@@ -266,6 +303,10 @@ class Template
$content = str_replace(array_keys($replace), array_values($replace), $content); $content = str_replace(array_keys($replace), array_values($replace), $content);
// 编译存储 // 编译存储
$this->storage->write($cacheFile, $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; return;
} }
@@ -361,15 +402,17 @@ class Template
$content = str_replace($matches[0], '', $content); $content = str_replace($matches[0], '', $content);
// 解析Layout标签 // 解析Layout标签
$array = $this->parseAttr($matches[0]); $array = $this->parseAttr($matches[0]);
//if (!C('LAYOUT_ON') || C('LAYOUT_NAME') != $array['name']) { if (!$this->config['layout_on'] || $this->config['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']; $layoutFile = $this->parseTemplateFile($array['name']);
if (is_file($layoutFile)) { if (is_file($layoutFile)) {
$replace = isset($array['replace']) ? $array['replace'] : $this->config['layout_item']; $replace = isset($array['replace']) ? $array['replace'] : $this->config['layout_item'];
// 替换布局的主体内容 // 替换布局的主体内容
$content = str_replace($replace, $content, file_get_contents($layoutFile)); $content = str_replace($replace, $content, file_get_contents($layoutFile));
// 记录布局文件的更新时间
$this->includeFile[$this->md5Key][filemtime($layoutFile)] = $layoutFile;
}
} }
//}
} else { } else {
$content = str_replace('{__NOLAYOUT__}', '', $content); $content = str_replace('{__NOLAYOUT__}', '', $content);
} }
@@ -397,7 +440,7 @@ class Template
foreach ($array as $k => $v) { foreach ($array as $k => $v) {
// 以$开头字符串转换成模板变量 // 以$开头字符串转换成模板变量
if (0 === strpos($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); $parseStr = str_replace('[' . $k . ']', $v, $parseStr);
} }
@@ -920,6 +963,8 @@ class Template
if (is_file($template)) { if (is_file($template)) {
// 获取模板文件内容 // 获取模板文件内容
$parseStr .= file_get_contents($template); $parseStr .= file_get_contents($template);
// 记录模板文件的更新时间
$this->includeFile[$this->md5Key][filemtime($template)] = $template;
} }
} }
return $parseStr; return $parseStr;

View File

@@ -15,7 +15,12 @@ use think\Exception;
class File class File
{ {
// 写入编译缓存 /**
* 写入编译缓存
* @string $cacheFile 缓存的文件名
* @string $content 缓存的内容
* @return void|array
*/
public function write($cacheFile, $content) 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)
{ {
if (!empty($vars) && is_array($vars)) {
// 模板阵列变量分解成为独立变量 // 模板阵列变量分解成为独立变量
extract($vars, EXTR_OVERWRITE); extract($vars, EXTR_OVERWRITE);
}
if ($isReturn) {
//载入模版缓存文件 //载入模版缓存文件
include $cacheFile; 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))) { foreach($files as $time => $path) {
if (is_file($path) && filemtime($path) > $time) {
// 模板文件如果有更新则缓存需要更新 // 模板文件如果有更新则缓存需要更新
return false; return false;
} elseif (0 != $cacheTime && time() > filemtime($cacheFile) + $cacheTime) { }
}
if (0 != $cacheTime && time() > filemtime($cacheFile) + $cacheTime) {
// 缓存是否在有效期 // 缓存是否在有效期
return false; return false;
} }

View File

@@ -35,7 +35,12 @@ class Sae
} }
} }
// 写入编译缓存 /**
* 写入编译缓存
* @string $cacheFile 缓存的文件名
* @string $content 缓存的内容
* @return void|array
*/
public function write($cacheFile, $content) 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); extract($vars, EXTR_OVERWRITE);
} }
if ($isReturn) {
return $this->get($cacheFile, 'content');
} else {
eval('?>' . $this->get($cacheFile, 'content')); eval('?>' . $this->get($cacheFile, 'content'));
} }
}
// 检查编译缓存是否有效 /**
* 检查编译缓存是否有效
* @array $template 用到的模板更新时间列表
* @string $cacheFile 缓存的文件名
* @int $cacheTime 缓存时间
* @return boolean
*/
public function check($template, $cacheFile, $cacheTime) public function check($template, $cacheFile, $cacheTime)
{ {
$mtime = $this->get($cacheFile, 'mtime'); foreach($template as $time => $path) {
if (!$this->get($cacheFile, 'content') || (is_file($template) && filemtime($template) > $mtime)) { if (is_file($path) && filemtime($path) > $time) {
// 模板文件如果有更新则缓存需要更新 // 模板文件如果有更新则缓存需要更新
return false; return false;
}if (0 != $cacheTime && time() > $mtime + $cacheTime) { }
}
$mtime = $this->get($cacheFile, 'mtime');
if (0 != $cacheTime && time() > $mtime + $cacheTime) {
// 缓存是否在有效期 // 缓存是否在有效期
return false; return false;
} else { } else {

View File

@@ -1,4 +1,4 @@
{layout name="layout" replace="[__REPLACE__]" /} {layout name="layout2" replace="[__REPLACE__]" /}
<div> <div>
{include file="include" name="info" value="$info.value" /} {include file="include" name="info" value="$info.value" /}
{block name="main"} {block name="main"}

View File

@@ -1 +1 @@
include2 {$info.value}:

View File

@@ -1,2 +1,2 @@
<nav>[__REPLACE__] <div>{__CONTENT__}
</nav> </div>

View File

@@ -0,0 +1,2 @@
<nav>[__REPLACE__]
</nav>

View File

@@ -247,47 +247,6 @@ EOF;
$this->assertEquals($data, $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() public function testThinkVar()
{ {
$config['tpl_begin'] = '{'; $config['tpl_begin'] = '{';
@@ -360,15 +319,47 @@ EOF;
public function testFetch() public function testFetch()
{ {
$template = new Template(); $config['view_path'] = dirname(__FILE__) . '/';
$template->view_path = dirname(__FILE__) . '/'; $config['view_suffix'] = '.html';
$data = ['name' => 'value']; $config['layout_on'] = true;
$content = <<<EOF $config['layout_name'] = 'layout';
{\$name} $template = new Template($config);
EOF; $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); $content = <<<EOF
$this->expectOutputString('value'); {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() public function testVarAssign()