📄 正在查看:twcms/kongphp/base/view.class.php
大小:7,070 字节 · 修改:2014-01-24 01:15:24 · 行数:193
1<?php
2/**
3 * Copyright (C) 2013-2014 www.kongphp.com All rights reserved.
4 * Licensed http://www.gnu.org/licenses/lgpl.html
5 * Author: wuzhaohuan <kongphp@gmail.com>
6 */
7
8class view{
9 private $vars = array(); //模板变量集合
10 private $head_arr = array(); //模板头部代码数组
11
12 public function __construct() {
13 $_ENV['_theme'] = 'default'; //主题目录
14 $_ENV['_view_diy'] = FALSE; //DIY模板解析是否开启
15 }
16
17 public function assign($k, &$v) {
18 $this->vars[$k] = &$v;
19 }
20
21 public function assign_value($k, $v) {
22 $this->vars[$k] = $v;
23 }
24
25 // 注意: 为安全考虑,$filename 尽量限制为 (英文 数字 _ .)
26 public function display($filename = null) {
27 $_ENV['_tplname'] = is_null($filename) ? $_GET['control'].'_'.$_GET['action'].'.htm' : $filename;
28 extract($this->vars, EXTR_SKIP);
29 include $this->get_tplfile($_ENV['_tplname']);
30 }
31
32 private function get_tplfile($filename) {
33 $view_dir = APP_NAME.($_ENV['_view_diy'] ? '_view_diy' : '_view').'/';
34 $php_file = RUNTIME_PATH.$view_dir.$_ENV['_theme'].','.$filename.'.php';
35
36 if(!is_file($php_file) || DEBUG) {
37 $tpl_file = core::get_original_file($filename, VIEW_PATH.$_ENV['_theme'].'/');
38
39 if(!$tpl_file) {
40 throw new Exception('模板文件 '.$_ENV['_theme'].'/'.$filename.' 不存在');
41 }
42
43 if(FW($php_file, $this->tpl_parse($tpl_file)) === false) {
44 throw new Exception("写入模板编译文件 $filename 失败");
45 }
46 }
47 return $php_file;
48 }
49
50 private function tpl_parse($tpl_file) {
51 //严格要求的变量和数组 $abc[a]['b']["c"][$d] 合法 $abc[$a[b]] 不合法
52 $reg_arr = '[a-zA-Z_]\w*(?:\[\w+\]|\[\'\w+\'\]|\[\"\w+\"\]|\[\$[a-zA-Z_]\w*\])*';
53
54 $s = file_get_contents($tpl_file);
55
56 //第1步 包含inc模板
57 $s = preg_replace_callback('#\{inc\:([\w\.]+)\}#', array($this, 'parse_inc'), $s);
58
59 //第2步 解析模板hook
60 $s = preg_replace_callback('#\{hook\:([\w\.]+)\}#', array('core', 'parse_hook'), $s);
61
62 //第3步 解析php代码
63 $s = preg_replace('#(?:\<\?.*?\?\>|\<\?.*)#s', '', $s); //清理掉PHP语法(目的统一规范)
64 $s = preg_replace('#\{php\}(.*?)\{\/php\}#s', '<?php \\1 ?>', $s);
65 //$s = preg_replace('#\{php\}.*?\{\/php\}#s', '', $s); //特殊需求,不想让模板支持PHP代码
66
67 //第4步 包含block
68 $s = preg_replace_callback('#\{block\:([a-zA-Z_]\w*)\040?([^\n\}]*?)\}(.*?){\/block}#s', array($this, 'parse_block'), $s);
69
70 //第5步 解析loop
71 while(preg_match('#\{loop\:\$'.$reg_arr.'(?:\040\$[a-zA-Z_]\w*){1,2}\}.*?\{\/loop\}#s', $s))
72 $s = preg_replace_callback('#\{loop\:(\$'.$reg_arr.'(?:\040\$[a-zA-Z_]\w*){1,2})\}(.*?)\{\/loop\}#s', array($this, 'parse_loop'), $s);
73
74 //第6步 解析if (未考虑安全过滤)
75 while(preg_match('#\{if\:[^\n\}]+\}.*?\{\/if\}#s', $s))
76 $s = preg_replace_callback('#\{if\:([^\n\}]+)\}(.*?)\{\/if\}#s', array($this, 'parse_if'), $s);
77
78 //第7步 解析变量
79 $s = preg_replace('#\{\@([^\}]+)\}#', '<?php echo(\\1); ?>', $s); //用于运算时的输出 如 {@$k+2}
80 $s = preg_replace_callback('#\{(\$'.$reg_arr.')\}#', array($this, 'parse_vars'), $s);
81
82 // $s = str_replace(array("\r\n", "\n", "\t"), '', $s); // 压缩HTML代码
83
84 //第8步 组合模板代码
85 $head_str = empty($this->head_arr) ? '' : implode("\r\n", $this->head_arr);
86 $s = "<?php defined('APP_NAME') || exit('Access Denied'); $head_str\r\n?>$s";
87 $s = str_replace('?><?php ', '', $s);
88
89 return $s;
90 }
91
92 private function parse_inc($matches) {
93 // 注意:在可视化设计时需要排除前缀 inc- 的模板,所以不能去掉前缀
94 $filename = 'inc-'.$matches[1];
95 $tpl_file = core::get_original_file($filename, VIEW_PATH.$_ENV['_theme'].'/');
96
97 if(!$tpl_file) {
98 throw new Exception('模板文件 '.$_ENV['_theme'].'/'.$filename.' 不存在');
99 }
100
101 return file_get_contents($tpl_file);
102 }
103
104 private function parse_block($matches) {
105 $func = $matches[1];
106 $config = $matches[2];
107 $s = $matches[3];
108
109 $lib_file = core::get_original_file('kp_block_'.$func.'.lib.php', BLOCK_PATH);
110 if(!is_file($lib_file)) return '';
111
112 //为减少IO,把需要用到的函数代码放到模板解析代码头部
113 $lib_str = file_get_contents($lib_file);
114 $lib_str = preg_replace_callback('#\t*\/\/\s*hook\s+([\w\.]+)[\r\n]#', array('core', 'parse_hook'), $lib_str);
115 if(!DEBUG) $lib_str = _strip_whitespace($lib_str);
116 $lib_str = core::clear_code($lib_str);
117 $this->head_arr['kp_block_'.$func] = $lib_str;
118
119 $s = $this->rep_double($s);
120 $config = $this->rep_double($config);
121
122 //解析设置数组并生成执行函数
123 $config_arr = array();
124 preg_match_all('#([a-zA-Z_]\w*)="(.*?)" #', $config.' ', $m);
125 foreach($m[2] as $k=>$v) {
126 if(isset($v)) $config_arr[strtolower($m[1][$k])] = addslashes($v);
127 }
128 unset($m);
129 $func_str = 'kp_block_'.$func.'('.var_export($config_arr, 1).');';
130
131 //-----------定义转换后的首尾代码-----------
132 $before = $after = '';
133 //公共块移到模板解析代码头部
134 if(substr($func, 0, 7) == 'global_') {
135 $this->head_arr[$func] = '$gdata = '.$func_str;
136 }else{
137 $before .= '<?php $data = '.$func_str.' ?>';
138 $after .= '<?php unset($data); ?>';
139 }
140 //DIY模板时才能用到
141 if($_ENV['_view_diy']) {
142 $this->kp_block_id++;
143 $before .= '<span kp_block_diy="before" kp_block_id="'.$this->kp_block_id.'"></span>';
144 $after .= '<span kp_block_diy="after" kp_block_id="'.$this->kp_block_id.'"></span>';
145 }
146 return $before.$s.$after;
147 }
148
149 //严格要求格式 {loop:$arr[a] $v $k}
150 private function parse_loop($matches) {
151 $args = explode(' ', $this->rep_double($matches[1]));
152 $s = $this->rep_double($matches[2]);
153
154 $arr = $this->rep_vars($args[0]);
155 $v = empty($args[1]) ? '$v' : $args[1];
156 $k = empty($args[2]) ? '' : $args[2].'=>';
157 return "<?php if(isset($arr) && is_array($arr)) { foreach($arr as $k&$v) { ?>$s<?php }} ?>";
158 }
159
160 private function parse_if($matches) {
161 $expr = $this->rep_double($matches[1]);
162 $expr = $this->rep_vars($expr);
163 $s = preg_replace_callback('#\{elseif\:([^\n\}]+)\}#', array($this, 'rep_elseif'), $this->rep_double($matches[2]));
164 $s = str_replace('{else}', '<?php }else{ ?>', $s);
165 return "<?php if ($expr) { ?>$s<?php } ?>";
166 }
167
168 private function rep_elseif($matches) {
169 $expr = $this->rep_double($matches[1]);
170 $expr = $this->rep_vars($expr);
171 return "<?php }elseif($expr) { ?>";
172 }
173
174 private function parse_vars($matches) {
175 $vars = $this->rep_double($matches[1]);
176 $vars = $this->rep_vars($vars);
177 return "<?php echo(isset($vars) ? $vars : ''); ?>";
178 }
179
180 //替换 " 号, 注意只能是 " 号
181 private function rep_double($s) {
182 return str_replace('\"', '"', $s);
183 }
184
185 //转$abc[a]['b']["c"][$d] 为 $abc['a']['b']['c'][$d]
186 private function rep_vars($s) {
187 $s = preg_replace('#\[(\w+)\]#', "['\\1']", $s);
188 $s = preg_replace('#\[\"(\w+)\"\]#', "['\\1']", $s);
189 $s = preg_replace('#\[\'(\d+)\'\]#', '[\\1]', $s);
190 return $s;
191 }
192}
193