Skip to content

Instantly share code, notes, and snippets.

@forthxu
Created February 13, 2015 09:42
Show Gist options
  • Select an option

  • Save forthxu/f9a94d9dfe8a8f8d2853 to your computer and use it in GitHub Desktop.

Select an option

Save forthxu/f9a94d9dfe8a8f8d2853 to your computer and use it in GitHub Desktop.
yaf基础模板类
<?php
namespace Util;
class YafTemplate implements \Yaf\View_Interface{
private $style = ''; //当前生效的风格,用于缓存目录
private $mConfig = array(
'path' => '', //模板目录,绝对地址即在当前地址上加模块目录,
'suffix' => 'html', //模板后缀
'divide' => '/', //模板分割符,控制器和方法中间的分割符,
'compile_suffix'=> '.php', //编译后缀
'ismd5' => 1, //编译时是否使用Md5加密缓存文件名
'style' => '', //模板风格 通常用于设置WAP风格,没有的时候返回默认风格
'defaultstyle' => '', //模板默认风格目录,不存在将报错
'tag_left' => '{', //左标签
'tag_right' => '}', //右标签
'cache_path' => '#runtime/cache' , //缓存目录
'isviewfilter' => 1, //是否对变量进行转换
//如果写到相对目录是什么情况?
);
/**
* 构造函数
* @param object $config 设置参数
*/
public function __construct($rConfig='') {
if(!$rConfig){
$rConfig = \base::config('application.view')->toArray();
if($cache_path=\base::config('application.path.cache'))
$this->mConfig['cache_path'] = $cache_path.'/template';
}
if($rConfig['ext']) $this->mConfig['suffix'] = $rConfig['ext'];
if($rConfig['divide']) $this->mConfig['divide'] = $rConfig['divide'];
if($rConfig['path']) $this->mConfig['path'] = $rConfig['path'];
if($rConfig['defaultstyle']) $this->mConfig['defaultstyle'] = $rConfig['defaultstyle'];
if($rConfig['style']) $this->mConfig['style'] = $rConfig['style'];
}
/**
* 引入模板,模板中使用 {template "文件","模块"}
* @param
*/
public function Template($name,$m='') {
$template_file_name = $this->getTemplateFile($name,$m);
$cache_file_name = $this->getCacheFile($name,$m);
if( !file_exists($cache_file_name) ||
(
file_exists($template_file_name) &&
filemtime($template_file_name) > filemtime($cache_file_name)
)
) {
$this->cache_template($template_file_name, $cache_file_name);
}
return $cache_file_name;
}
/**
* 设置视图模板的目录 该类继承 Yaf_View_Interface 必须存在
* @param string $template_path
*/
public function setScriptPath($template_path){
$this->mConfig['path'] = $template_path;
}
/**
* 获取视图模板的目录 该类继承 Yaf_View_Interface 必须存在
* @return string
*/
public function getScriptPath(){
return $this->mConfig['path'];
}
/**
* 自动实例化模版 该类继承 Yaf_View_Interface 必须存在
* @param string $template
*/
public function render($template,$value=null) {
//去除自动渲染模版带上后缀问题
$template = preg_replace('/.'.$this->mConfig['suffix'].'/', '', $template);
//生成模板路径
$template = $this->Template($template);
//获取当前可用变量,
$tVar = \base::registry('','Template');
//对模板变量进行过滤
if ($this->mConfig['isviewfilter']) $this->out_put($tVar);
foreach ($tVar as $key => $value) {
$$key = $value;
}
include $template ;
}
/**
* 手动显示视图,和render自动调用不一样,本方法被调用即关闭自动视图功能,
* 使用方法:$this->view->display();
* @param string $template
* @return array
*/
public function display($template='', $value = null) {
\YAF\Dispatcher::getInstance()->autoRender(FALSE);
if(empty($template)){
$mca = \base::registry('Trigger','Route');
$template = strtolower($mca['controller']).'/'.$mca['action'];
}
if(!empty($value)) $this->tVar = $value;
$this->render($template);
}
/**
* 设置模板变量
* 模板赋值核心函数,$key => $value , 模板变量名称 => 控制器中变量
* Controller中使用方法:$this->view->assign($key, $value);
* @param string $key
* @param string $value
*/
public function assign($name, $value = null){
\base::registry(array($name=>$value),'Template');
}
/**
* 模板编译-缓存模板
* @param string $template_file_name 源文件名称
* @param string $cache_file_name 目标文件
* @return void
*/
private function cache_template($template_file_name, $cache_file_name) {
if(is_dir($path = dirname($cache_file_name)) == false){ //判断目录是否存在
$this->create_dir($path);
}
if(!file_exists($template_file_name)) \base::error($template_file_name . ' 文件不存在!');
//得到文件
$str = @file_get_contents($template_file_name);
//替换标签
$str = $this->replace_tag($str);
//写入缓存文件
$ret = @file_put_contents($cache_file_name, $str);
if ($ret == false) {
\base::error($cache_file_name . ' 写入失败!');
}
}
/**
* 模板编译-标签正则替换
* @param string $str 模板文件数据
* @return string
*/
private function replace_tag($str) {
return $this->simple($str, $this->mConfig['tag_left'], $this->mConfig['tag_right']); //编译
}
/**
* 得到模板文件
* @param object $config 设置参数
*/
public function getTemplateFile($name,$m='') {
//分隔符的转换
if(!empty($this->mConfig['divide']) && $this->mConfig['divide'] !=DS){
$name = str_replace (DS,$this->mConfig['divide'],$name);
}else if(empty($this->mConfig['divide'])){
//如果没有分隔符,是不是应该去掉控制器呢?
}
$file_name = $name.'.'.$this->mConfig['suffix'];
//获取当前模块
if(empty($m)) $m = \base::registry('Trigger.Module','Route');
//模板目录是相对目录,模板文件位于模块下的风格目录中
if(!is_dir($this->mConfig['path'])){
$models_path = \base::config('application.path.modules');
$path = $models_path.DS.$m.DS.$this->mConfig['path'];
$path = str_replace ('/',DS,$path);
}else{ //模板目录是绝对目录,文件位于模板目录下的风格包中的模块文件里
$path = $this->mConfig['path'];
$file_name = $m.DS.$file_name;
}
$style = '';
//判断风格目录文件是否存在
if(!empty($this->mConfig['style'])){
$template_path = $path.DS.$this->mConfig['style'].DS.$file_name;
if(is_file($template_path)){
$style = $this->mConfig['style'];
}
}
if(empty($style) && !empty($this->mConfig['defaultstyle'])){
$template_path = $path.DS.$this->mConfig['defaultstyle'].DS.$file_name;
if(is_file($template_path)){
$style = $this->mConfig['defaultstyle'];
}
}else{
//风格获取失败,生成默认的模板(不带风格的)
if(empty($style)) $template_path = $path .DS.$file_name;
}
if(!is_file($template_path)){
\base::error($name.'模板文件不存在');
}
$this->style = $style;
return $template_path;
}
/**
* 得到缓存文件路径
*/
public function getCacheFile($name,$m='') {
$cache_path = $this->mConfig['cache_path'];
if(!empty($this->style)){
$cache_path .= '/'.$this->style;
}
//获取当前模块
if(empty($m)) $m = \base::registry('Trigger.Module','Route');
$name = "$m/$name";
if($this->mConfig['ismd5']){
//MD5的模板不需要分目录.全扔到一个风格目录下就行了
$name = md5($cache_path.$name);
}
$path = $cache_path.'/'.$name.$this->mConfig['compile_suffix'];
// $path = str_replace ('/',DS,$path);
$this->create_dir(dirname($path));
return $path;
}
/**
* 创建目录
* @param string $path 目录
* @return
*/
private function create_dir($path) {
if (is_dir($path)) return true;
$this->create_dir(dirname($path));
@mkdir($path);
@chmod($path, 0777);
return true;
}
/**
* 模板驱动-简单的驱动
* @param string $str 模板文件数据
* @return string
*/
private function simple($str, $left, $right) {
$pregs = array(
//规则,替换值,修饰符,修饰符在更高级的PHP环境中,需要再来处理一次 TODO
array('\/\*(.+?)\*\/','','s'), //清除注释
array('\/\/(.+?)','',''),
array(':(.+?)','<?php echo \1;?>',''), //函数 {:func()}
//变量
array('(\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_$\x7f-\xff\[\]\'\']*)','<?php echo \1;?>','s'),
//全局变量
array('([A-Z_\x7f-\xff][A-Z0-9_\x7f-\xff]*)','<?php echo \1;?>',''),
//循环
array('loop((\w+)?)\s+(\S+)\s+(\S+)','<?php $n\2=1;if(is_array(\3)) foreach(\3 AS \4) { ?>',''),
array('loop((\w+)?)\s+(\S+)\s+(\S+)\s+(\S+)','<?php $n\2=1; if(is_array(\3)) foreach(\3 AS \4 => \5) { ?>',''),
array('\/loop((\w+)?)','<?php $n\2++;}unset($n\2); ?>',''),
array('if\s+(.+?)','<?php if(\1) { ?>',''),
array('else','<?php } else { ?>',''),
array('elseif\s+(.+?)','<?php } elseif (\1) { ?>',''),
array('\/if','<?php } ?>',''),
//PHP代码
array('php\s+(.+)','<?php \\1?>',''),
//调用模板
array('template\s+(.+)','<?php include $this->Template(\1); ?>',''),
//引用文件,
array('include\s+(.+)','<?php include \1; ?>',''),
);
//代码块
$str = preg_replace ( "/\<php\>(.+?)\<\/php\>/s", "<?php \\1?>", $str);
foreach ($pregs as $value) {
$key = "/".$left.$value[0].$right.'/'.$value[2];
$str = preg_replace($key,$value[1],$str);
}
//合并多行PHP代码
$str = preg_replace ( "/\?>((\s+)?)<\?php/", "", $str);
//压缩代码
$str = $this->compress_html($str);
return $str;
}
/**
* 模板-模板变量输出过滤
* @param array $arr 视图存放器数组
* @return array
*/
private function out_put(&$value) {
$value = (array) $value;
foreach ($value as $key => $val) {
if (is_array($val)) {
$this->out_put($value[$key]);
} elseif (is_object($val)) {
$value[$key] = $val;
} else {
if (function_exists('htmlspecialchars')) {
$value[$key] = htmlspecialchars($val);
} else {
$value[$key] = str_replace(array("&", '"', "'", "<", ">", "%3C", "%3E"), array("&amp;", "&quot;", "&#039;", "&lt;", "&gt;", "&lt;", "&gt;"), $val);
}
}
}
}
/**
* 压缩html : 清除换行符,清除制表符,去掉多余空格
* @param $string
* @return 压缩后的$string
* */
function compress_html($string) {
$string = str_replace("\r\n", '', $string); //清除换行符
$string = str_replace("\n", '', $string); //清除换行符
$string = str_replace("\t", '', $string); //清除制表符
$pattern = array (
"/>\s+</Um",
"/>(\s+\n|\r)/",
"/[\s]+/",
);
$replace = array ('><', '>', ' ');
return preg_replace($pattern, $replace, $string);
}
//更安全的压缩
//启城 2015-1-28 1:32:33 未完成的压缩,,丢失了 /HTML
function compress_html2($html) {
$segments = preg_split("/(<[^>]+?>)/si",$html, -1,PREG_SPLIT_NO_EMPTY| PREG_SPLIT_DELIM_CAPTURE);
$compressed = array();
$stack = array();
$tag = '';
$half_open = array('meta','input','link','img','br');
$cannot_compress = array('pre','code','script');
// P($segments);
foreach($segments as $seg)
{
if(trim($seg) === '')
{
continue;
}
//<.../>
if(preg_match("!<([a-z0-9]+)[^>]*?/>!si",$seg, $match))
{
//$tag = self::format_tag($match[1]);
$this->format_tag($match[1]);
$compressed[] = $seg;
}
else if(preg_match("!</([a-z0-9]+)[^>]*?>!si",$seg,$match))//</..>
{
$tag = $this->format_tag($match[1]);
if(count($stack) > 0 && $stack[count($stack)-1] == $tag)
{
array_pop($stack);
$compressed[] = $seg;
}
//这里再最好加一段判断,可以用于修复错误的html
//...
}
else if(preg_match("!<([a-z0-9]+)[^>]*?>!si",$seg,$match))//<>
{
$tag = $this->format_tag($match[1]);
//半闭合标签不需要入栈,如<br/>,<img/>
if(!in_array($tag, $half_open))
{
array_push($stack,$tag);
}
// P($seg);die;
$compressed[] = $seg;
}
else if(preg_match("~<![^>]*>~", $seg))
{
//文档声明和注释,注释也不能删除,如<!--ie条件-->
$compressed[] = $seg;
}
else if(preg_match("/[^<\?php](.*)\?>/", $seg))
{
//php 代码不可以删除
$compressed[] = $seg;
}
else
{
// P($tag);
$compressed[] = in_array($tag, $cannot_compress) ? $seg : preg_replace('!\s!', '', $seg);
}
}
// P($compressed);die;
$html = join('',$compressed);
return $html;
}
function format_tag($tag)
{
return trim(strtolower($tag));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment