全部博文(42)
分类:
2008-09-15 01:17:10
<?php
/**
* 模板类
*
* @category XD
* @package XD_View
* @copyright Copyright (c) 2008
* @license New BSD License
* @version $Id: XD/View.php 版本号 2008-09-03 02:06:xx epaddy $
*/
require_once 'XD/View/Exception.php';
/**
* 视图
*
*
*/
final class XD_View {
public $force = 0; // 是否每次都编译模板
public $merge = 0; // 是否合并模板
private $_language = array();
private $_tplDir = 'view/default';
private $_compliedDir = 'data/view';
private $_tplFile = '';
private $_compliedFile = '';
private $_vars = array();
private $_tplId = '';
const VAR_REGEXP = "(\@?\\\$[a-zA-Z_][\\\$\w]*(?:\[[\w\-\.\"\'\[\]\$]+\])*)";
const CONST_REGEXP = "/\{([\w]+)\}/";
public function __construct() {
}
public function setTplDir($dir) {
$this->_tplDir = trim($dir, '/');
}
public function setCompliedDir($dir) {
$this->_compliedDir = trim($dir, '/');
}
/**
* 设置语言,将把$this->_language覆盖掉
*
* @param array $lang 语言数组
*/
public function setLang($lang) {
$this->_language = (array)$lang;
}
/**
* 添加语言项
*
* @param array $lang
*/
public function addLang($lang) {
$this->_language = array_merge($this->_language, (array)$lang);
}
/**
* 模板变量赋值
*
* @param string $k 模板变量下标
* @param mixed $v 模板变量值
*/
public function assign($k, $v) {
$this->_vars[$k] = $v;
}
/**
* 显示文件
*
* @param string $file 模板文件
*/
public function display($file, $tplId = '') {
$this->_tplId = $tplId;
extract($this->_vars, EXTR_SKIP);
include $this->_getTpl($file);
}
/**
* 获取模板
*
* @param string $file 模板文件名
* @return string 编译后的模板文件
*/
private function _getTpl($file) {
$this->_tplFile = BASE_DIR . "/{$this->_tplDir}/{$file}.htm";
$this->_compliedFile = BASE_DIR . "/{$this->_compliedDir}/{$this->_tplId}_{$file}.php";
// 判断是否强制编译或是否过期($this->_compliedFile不存在时 < 成立)
if($this->force || @filemtime($this->_compliedFile) < @filemtime($this->_tplFile)) {
$this->_complie();
}
return $this->_compliedFile;
}
/**
* 编译模板
*
* @throws XD_View_Exception 如果模板文件不存在则抛出异常
*
*/
private function _complie() {
if(!$template = file_get_contents($this->_tplFile)) {
throw new XD_View_Exception($this->_tplFile . ' does not exists!');
}
// 包含另一个模板
// {tpl xx}
$template = preg_replace("/\{tpl\s+['\"]?(.*?)['\"]?\}/ise", "\$this->_subTpl('\\1')", $template);
// 去掉的
$template = preg_replace("/\<\!\-\-\{(.+?)\}\-\-\>/s", "{\\1}", $template);
// 处理语言
// {lang key} {lang key1 key2}
$template = preg_replace("/\{lang\s+(\w+?)\s+(\w+?)\}/ise", "\$this->_lang('\\1', '\\2')", $template);
$template = preg_replace("/\{lang\s+(\w+?)\}/ise", "\$this->_lang('\\1')", $template);
// 把php代码块进行base64编码,使其中的变量不被处理
// {#xx#}
$template = preg_replace("/\{#(.+?)#\}/ies", "'{base64 ' . base64_encode('\\1') . '}'", $template);
// 处理变量
// {$var}
$template = preg_replace("/\{" . self::VAR_REGEXP . "\}/", "", $template);
// {if 表达式}
$template = preg_replace("/\{if\s+(.+?)\}/is", "", $template);
// {elseif 表达式}
$template = preg_replace("/\{elseif\s+(.+?)\}/is", "", $template);
// {else}
$template = preg_replace("/\{else\}/is", "", $template);
// {for 表达式1; 表达式2; 表达式3}
$template = preg_replace("/\{for\s+(.*?)\}/is", "", $template);
// endif
// {/if}
$template = preg_replace("/\{\/if\}/is", "", $template);
// endfor
// {/for}
$template = preg_replace("/\{\/for\}/is", "", $template);
// foreach
for($i=0; $i<5; $i++) {
// {loop $xx $xx}
$template = preg_replace("/\{loop\s+" . self::VAR_REGEXP . "\s+" . self::VAR_REGEXP . "\s+" . self::VAR_REGEXP . "\}(.+?)\{\/loop\}/ies", "\$this->_loopSection('\\1', '\\2', '\\3', '\\4')", $template);
// {loop $xx $xx $xx}
$template = preg_replace("/\{loop\s+" . self::VAR_REGEXP . "\s+" . self::VAR_REGEXP . "\}(.+?)\{\/loop\}/ies", "\$this->_loopSection('\\1', '', '\\2', '\\3')", $template);
}
// {CONST}
$template = preg_replace(self::CONST_REGEXP, "", $template);// {else} 也符合常量格式,这必须要在{else}后面
$template = preg_replace("/\{base64\s(.+?)\}/ise", "''", $template); // 还原页面中的php脚本
$template = preg_replace("/(\\\$[a-zA-Z_]\w+\[)([a-zA-Z_]\w+)\]/i", "\\1'\\2']", $template); // $x[x] => $x['x']
// {url xx}
$template = preg_replace("/\{url\s+['\"]?(.*?)['\"]?\}/is", "_url(\"\\1\");?>", $template);
$template = str_replace('?>, "\r\n", $template);
// 添加在模板顶部的信息
$thisTplMsg = ";
$thisTplMsg.= " * XD Web App Framework Template\r\n";
$thisTplMsg.= " *\r\n";
$thisTplMsg.= " * made file: {$this->_compliedFile}\r\n";
$thisTplMsg.= " * make from: {$this->_tplFile};\r\n";
$thisTplMsg.= " * make time: ". microtime(1) . ";\r\n";
$thisTplMsg.= " * make by XD template engine at " . date('Y-m-d H:i:s') . "\r\n";
$thisTplMsg.= " */\r\n";
$thisTplMsg.= "if(!defined('XD_IN')) exit('Access Denied');\r\n";
$thisTplMsg.= "?>";
$template = $thisTplMsg . $template;
$fp = fopen($this->_compliedFile, 'w');
flock($fp, LOCK_EX);
fwrite($fp, $template);
flock($fp, LOCK_UN);
fclose($fp);
}
private function _loopSection($arr, $k, $v, $statement) {
$statement = str_replace("\\\"", '"', $statement);
return $k ? "$v) :?>$statement" : "$statement";
}
/**
* 语言包处理
*
* @param string $k1 下标1
* @param string $k2 下标2
* @return string 语言包中该数组变量的值
*/
private function _lang($k1, $k2 = null) {
if ($k2) {
return !empty($this->_language[$k1][$k2]) ? $this->_language[$k1][$k2] : "{ {$k1}-{$k2} }";
} else {
return !empty($this->_language[$k1]) ? $this->_language[$k1] : "{ $k1 }";
}
}
private function _url($url){
if($GLOBALS['config']['clearUrl']) {
return XD_Util_Encoder::encode($url) . '.htm';
} else {
return '?' . $url;
}
}
private function _subTpl($subTpl) {
if ($this->merge) {
$content = file_get_contents(BASE_DIR . "/{$this->_tplDir}/{$subTpl}.htm");
// 多级包含,尽管支持多级包含,但应该不用多级包含
for ($i=0; $i<5; $i++) {
$content = preg_replace("/\{tpl\s+['\"]?(.*?)['\"]?\}/ise", "file_get_contents(BASE_DIR . \"/{$this->_tplDir}/\\1.htm\")", $content); // {tpl xx}
}
return $content;
} else {
return "_getTpl('{$subTpl}');?>";
}
}
}