Template.class.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: liu21st <liu21st@gmail.com>
  10. // +----------------------------------------------------------------------
  11. namespace Think;
  12. /**
  13. * ThinkPHP内置模板引擎类
  14. * 支持XML标签和普通标签的模板解析
  15. * 编译型模板引擎 支持动态缓存
  16. */
  17. class Template {
  18. // 模板页面中引入的标签库列表
  19. protected $tagLib = array();
  20. // 当前模板文件
  21. protected $templateFile = '';
  22. // 模板变量
  23. public $tVar = array();
  24. public $config = array();
  25. private $literal = array();
  26. private $block = array();
  27. /**
  28. * 架构函数
  29. * @access public
  30. */
  31. public function __construct(){
  32. $this->config['cache_path'] = C('CACHE_PATH');
  33. $this->config['template_suffix'] = C('TMPL_TEMPLATE_SUFFIX');
  34. $this->config['cache_suffix'] = C('TMPL_CACHFILE_SUFFIX');
  35. $this->config['tmpl_cache'] = C('TMPL_CACHE_ON');
  36. $this->config['cache_time'] = C('TMPL_CACHE_TIME');
  37. $this->config['taglib_begin'] = $this->stripPreg(C('TAGLIB_BEGIN'));
  38. $this->config['taglib_end'] = $this->stripPreg(C('TAGLIB_END'));
  39. $this->config['tmpl_begin'] = $this->stripPreg(C('TMPL_L_DELIM'));
  40. $this->config['tmpl_end'] = $this->stripPreg(C('TMPL_R_DELIM'));
  41. $this->config['default_tmpl'] = C('TEMPLATE_NAME');
  42. $this->config['layout_item'] = C('TMPL_LAYOUT_ITEM');
  43. }
  44. private function stripPreg($str) {
  45. return str_replace(
  46. array('{','}','(',')','|','[',']','-','+','*','.','^','?'),
  47. array('\{','\}','\(','\)','\|','\[','\]','\-','\+','\*','\.','\^','\?'),
  48. $str);
  49. }
  50. // 模板变量获取和设置
  51. public function get($name) {
  52. if(isset($this->tVar[$name]))
  53. return $this->tVar[$name];
  54. else
  55. return false;
  56. }
  57. public function set($name,$value) {
  58. $this->tVar[$name]= $value;
  59. }
  60. /**
  61. * 加载模板
  62. * @access public
  63. * @param string $tmplTemplateFile 模板文件
  64. * @param array $templateVar 模板变量
  65. * @param string $prefix 模板标识前缀
  66. * @return void
  67. */
  68. public function fetch($templateFile,$templateVar,$prefix='') {
  69. $this->tVar = $templateVar;
  70. $templateCacheFile = $this->loadTemplate($templateFile,$prefix);
  71. Storage::load($templateCacheFile,$this->tVar,null,'tpl');
  72. }
  73. /**
  74. * 加载主模板并缓存
  75. * @access public
  76. * @param string $tmplTemplateFile 模板文件
  77. * @param string $prefix 模板标识前缀
  78. * @return string
  79. * @throws ThinkExecption
  80. */
  81. public function loadTemplate ($tmplTemplateFile,$prefix='') {
  82. if(is_file($tmplTemplateFile)) {
  83. $this->templateFile = $tmplTemplateFile;
  84. // 读取模板文件内容
  85. $tmplContent = file_get_contents($tmplTemplateFile);
  86. }else{
  87. $tmplContent = $tmplTemplateFile;
  88. }
  89. // 根据模版文件名定位缓存文件
  90. $tmplCacheFile = $this->config['cache_path'].$prefix.md5($tmplTemplateFile).$this->config['cache_suffix'];
  91. // 判断是否启用布局
  92. if(C('LAYOUT_ON')) {
  93. if(false !== strpos($tmplContent,'{__NOLAYOUT__}')) { // 可以单独定义不使用布局
  94. $tmplContent = str_replace('{__NOLAYOUT__}','',$tmplContent);
  95. }else{ // 替换布局的主体内容
  96. $layoutFile = THEME_PATH.C('LAYOUT_NAME').$this->config['template_suffix'];
  97. // 检查布局文件
  98. if(!is_file($layoutFile)) {
  99. E(L('_TEMPLATE_NOT_EXIST_').':'.$layoutFile);
  100. }
  101. $tmplContent = str_replace($this->config['layout_item'],$tmplContent,file_get_contents($layoutFile));
  102. }
  103. }
  104. // 编译模板内容
  105. $tmplContent = $this->compiler($tmplContent);
  106. Storage::put($tmplCacheFile,trim($tmplContent),'tpl');
  107. return $tmplCacheFile;
  108. }
  109. /**
  110. * 编译模板文件内容
  111. * @access protected
  112. * @param mixed $tmplContent 模板内容
  113. * @return string
  114. */
  115. protected function compiler($tmplContent) {
  116. //模板解析
  117. $tmplContent = $this->parse($tmplContent);
  118. // 还原被替换的Literal标签
  119. $tmplContent = preg_replace_callback('/<!--###literal(\d+)###-->/is', array($this, 'restoreLiteral'), $tmplContent);
  120. // 添加安全代码
  121. $tmplContent = '<?php if (!defined(\'THINK_PATH\')) exit();?>'.$tmplContent;
  122. // 优化生成的php代码
  123. $tmplContent = str_replace('?><?php','',$tmplContent);
  124. // 模版编译过滤标签
  125. Hook::listen('template_filter',$tmplContent);
  126. return strip_whitespace($tmplContent);
  127. }
  128. /**
  129. * 模板解析入口
  130. * 支持普通标签和TagLib解析 支持自定义标签库
  131. * @access public
  132. * @param string $content 要解析的模板内容
  133. * @return string
  134. */
  135. public function parse($content) {
  136. // 内容为空不解析
  137. if(empty($content)) return '';
  138. $begin = $this->config['taglib_begin'];
  139. $end = $this->config['taglib_end'];
  140. // 检查include语法
  141. $content = $this->parseInclude($content);
  142. // 检查PHP语法
  143. $content = $this->parsePhp($content);
  144. // 首先替换literal标签内容
  145. $content = preg_replace_callback('/'.$begin.'literal'.$end.'(.*?)'.$begin.'\/literal'.$end.'/is', array($this, 'parseLiteral'),$content);
  146. // 获取需要引入的标签库列表
  147. // 标签库只需要定义一次,允许引入多个一次
  148. // 一般放在文件的最前面
  149. // 格式:<taglib name="html,mytag..." />
  150. // 当TAGLIB_LOAD配置为true时才会进行检测
  151. if(C('TAGLIB_LOAD')) {
  152. $this->getIncludeTagLib($content);
  153. if(!empty($this->tagLib)) {
  154. // 对导入的TagLib进行解析
  155. foreach($this->tagLib as $tagLibName) {
  156. $this->parseTagLib($tagLibName,$content);
  157. }
  158. }
  159. }
  160. // 预先加载的标签库 无需在每个模板中使用taglib标签加载 但必须使用标签库XML前缀
  161. if(C('TAGLIB_PRE_LOAD')) {
  162. $tagLibs = explode(',',C('TAGLIB_PRE_LOAD'));
  163. foreach ($tagLibs as $tag){
  164. $this->parseTagLib($tag,$content);
  165. }
  166. }
  167. // 内置标签库 无需使用taglib标签导入就可以使用 并且不需使用标签库XML前缀
  168. $tagLibs = explode(',',C('TAGLIB_BUILD_IN'));
  169. foreach ($tagLibs as $tag){
  170. $this->parseTagLib($tag,$content,true);
  171. }
  172. //解析普通模板标签 {$tagName}
  173. $content = preg_replace_callback('/('.$this->config['tmpl_begin'].')([^\d\w\s'.$this->config['tmpl_begin'].$this->config['tmpl_end'].'].+?)('.$this->config['tmpl_end'].')/is', array($this, 'parseTag'),$content);
  174. return $content;
  175. }
  176. // 检查PHP语法
  177. protected function parsePhp($content) {
  178. if(ini_get('short_open_tag')){
  179. // 开启短标签的情况要将<?标签用echo方式输出 否则无法正常输出xml标识
  180. $content = preg_replace('/(<\?(?!php|=|$))/i', '<?php echo \'\\1\'; ?>'."\n", $content );
  181. }
  182. // PHP语法检查
  183. if(C('TMPL_DENY_PHP') && false !== strpos($content,'<?php')) {
  184. E(L('_NOT_ALLOW_PHP_'));
  185. }
  186. return $content;
  187. }
  188. // 解析模板中的布局标签
  189. protected function parseLayout($content) {
  190. // 读取模板中的布局标签
  191. $find = preg_match('/'.$this->config['taglib_begin'].'layout\s(.+?)\s*?\/'.$this->config['taglib_end'].'/is',$content,$matches);
  192. if($find) {
  193. //替换Layout标签
  194. $content = str_replace($matches[0],'',$content);
  195. //解析Layout标签
  196. $array = $this->parseXmlAttrs($matches[1]);
  197. if(!C('LAYOUT_ON') || C('LAYOUT_NAME') !=$array['name'] ) {
  198. // 读取布局模板
  199. $layoutFile = THEME_PATH.$array['name'].$this->config['template_suffix'];
  200. $replace = isset($array['replace'])?$array['replace']:$this->config['layout_item'];
  201. // 替换布局的主体内容
  202. $content = str_replace($replace,$content,file_get_contents($layoutFile));
  203. }
  204. }else{
  205. $content = str_replace('{__NOLAYOUT__}','',$content);
  206. }
  207. return $content;
  208. }
  209. // 解析模板中的include标签
  210. protected function parseInclude($content, $extend = true) {
  211. // 解析继承
  212. if($extend)
  213. $content = $this->parseExtend($content);
  214. // 解析布局
  215. $content = $this->parseLayout($content);
  216. // 读取模板中的include标签
  217. $find = preg_match_all('/'.$this->config['taglib_begin'].'include\s(.+?)\s*?\/'.$this->config['taglib_end'].'/is',$content,$matches);
  218. if($find) {
  219. for($i=0;$i<$find;$i++) {
  220. $include = $matches[1][$i];
  221. $array = $this->parseXmlAttrs($include);
  222. $file = $array['file'];
  223. unset($array['file']);
  224. $content = str_replace($matches[0][$i],$this->parseIncludeItem($file,$array,$extend),$content);
  225. }
  226. }
  227. return $content;
  228. }
  229. // 解析模板中的extend标签
  230. protected function parseExtend($content) {
  231. $begin = $this->config['taglib_begin'];
  232. $end = $this->config['taglib_end'];
  233. // 读取模板中的继承标签
  234. $find = preg_match('/'.$begin.'extend\s(.+?)\s*?\/'.$end.'/is',$content,$matches);
  235. if($find) {
  236. //替换extend标签
  237. $content = str_replace($matches[0],'',$content);
  238. // 记录页面中的block标签
  239. preg_replace_callback('/'.$begin.'block\sname=[\'"](.+?)[\'"]\s*?'.$end.'(.*?)'.$begin.'\/block'.$end.'/is', array($this, 'parseBlock'),$content);
  240. // 读取继承模板
  241. $array = $this->parseXmlAttrs($matches[1]);
  242. $content = $this->parseTemplateName($array['name']);
  243. $content = $this->parseInclude($content, false); //对继承模板中的include进行分析
  244. // 替换block标签
  245. $content = $this->replaceBlock($content);
  246. }else{
  247. $content = preg_replace_callback('/'.$begin.'block\sname=[\'"](.+?)[\'"]\s*?'.$end.'(.*?)'.$begin.'\/block'.$end.'/is', function($match){return stripslashes($match[2]);}, $content);
  248. }
  249. return $content;
  250. }
  251. /**
  252. * 分析XML属性
  253. * @access private
  254. * @param string $attrs XML属性字符串
  255. * @return array
  256. */
  257. private function parseXmlAttrs($attrs) {
  258. $xml = '<tpl><tag '.$attrs.' /></tpl>';
  259. $xml = simplexml_load_string($xml);
  260. if(!$xml)
  261. E(L('_XML_TAG_ERROR_'));
  262. $xml = (array)($xml->tag->attributes());
  263. $array = array_change_key_case($xml['@attributes']);
  264. return $array;
  265. }
  266. /**
  267. * 替换页面中的literal标签
  268. * @access private
  269. * @param string $content 模板内容
  270. * @return string|false
  271. */
  272. private function parseLiteral($content) {
  273. if(is_array($content)) $content = $content[1];
  274. if(trim($content)=='') return '';
  275. //$content = stripslashes($content);
  276. $i = count($this->literal);
  277. $parseStr = "<!--###literal{$i}###-->";
  278. $this->literal[$i] = $content;
  279. return $parseStr;
  280. }
  281. /**
  282. * 还原被替换的literal标签
  283. * @access private
  284. * @param string $tag literal标签序号
  285. * @return string|false
  286. */
  287. private function restoreLiteral($tag) {
  288. if(is_array($tag)) $tag = $tag[1];
  289. // 还原literal标签
  290. $parseStr = $this->literal[$tag];
  291. // 销毁literal记录
  292. unset($this->literal[$tag]);
  293. return $parseStr;
  294. }
  295. /**
  296. * 记录当前页面中的block标签
  297. * @access private
  298. * @param string $name block名称
  299. * @param string $content 模板内容
  300. * @return string
  301. */
  302. private function parseBlock($name,$content = '') {
  303. if(is_array($name)){
  304. $content = $name[2];
  305. $name = $name[1];
  306. }
  307. $this->block[$name] = $content;
  308. return '';
  309. }
  310. /**
  311. * 替换继承模板中的block标签
  312. * @access private
  313. * @param string $content 模板内容
  314. * @return string
  315. */
  316. private function replaceBlock($content){
  317. static $parse = 0;
  318. $begin = $this->config['taglib_begin'];
  319. $end = $this->config['taglib_end'];
  320. $reg = '/('.$begin.'block\sname=[\'"](.+?)[\'"]\s*?'.$end.')(.*?)'.$begin.'\/block'.$end.'/is';
  321. if(is_string($content)){
  322. do{
  323. $content = preg_replace_callback($reg, array($this, 'replaceBlock'), $content);
  324. } while ($parse && $parse--);
  325. return $content;
  326. } elseif(is_array($content)){
  327. if(preg_match('/'.$begin.'block\sname=[\'"](.+?)[\'"]\s*?'.$end.'/is', $content[3])){ //存在嵌套,进一步解析
  328. $parse = 1;
  329. $content[3] = preg_replace_callback($reg, array($this, 'replaceBlock'), "{$content[3]}{$begin}/block{$end}");
  330. return $content[1] . $content[3];
  331. } else {
  332. $name = $content[2];
  333. $content = $content[3];
  334. $content = isset($this->block[$name]) ? $this->block[$name] : $content;
  335. return $content;
  336. }
  337. }
  338. }
  339. /**
  340. * 搜索模板页面中包含的TagLib库
  341. * 并返回列表
  342. * @access public
  343. * @param string $content 模板内容
  344. * @return string|false
  345. */
  346. public function getIncludeTagLib(& $content) {
  347. //搜索是否有TagLib标签
  348. $find = preg_match('/'.$this->config['taglib_begin'].'taglib\s(.+?)(\s*?)\/'.$this->config['taglib_end'].'\W/is',$content,$matches);
  349. if($find) {
  350. //替换TagLib标签
  351. $content = str_replace($matches[0],'',$content);
  352. //解析TagLib标签
  353. $array = $this->parseXmlAttrs($matches[1]);
  354. $this->tagLib = explode(',',$array['name']);
  355. }
  356. return;
  357. }
  358. /**
  359. * TagLib库解析
  360. * @access public
  361. * @param string $tagLib 要解析的标签库
  362. * @param string $content 要解析的模板内容
  363. * @param boolean $hide 是否隐藏标签库前缀
  364. * @return string
  365. */
  366. public function parseTagLib($tagLib,&$content,$hide=false) {
  367. $begin = $this->config['taglib_begin'];
  368. $end = $this->config['taglib_end'];
  369. if(strpos($tagLib,'\\')){
  370. // 支持指定标签库的命名空间
  371. $className = $tagLib;
  372. $tagLib = substr($tagLib,strrpos($tagLib,'\\')+1);
  373. }else{
  374. $className = 'Think\\Template\TagLib\\'.ucwords($tagLib);
  375. }
  376. $tLib = \Think\Think::instance($className);
  377. $that = $this;
  378. foreach ($tLib->getTags() as $name=>$val){
  379. $tags = array($name);
  380. if(isset($val['alias'])) {// 别名设置
  381. $tags = explode(',',$val['alias']);
  382. $tags[] = $name;
  383. }
  384. $level = isset($val['level'])?$val['level']:1;
  385. $closeTag = isset($val['close'])?$val['close']:true;
  386. foreach ($tags as $tag){
  387. $parseTag = !$hide? $tagLib.':'.$tag: $tag;// 实际要解析的标签名称
  388. if(!method_exists($tLib,'_'.$tag)) {
  389. // 别名可以无需定义解析方法
  390. $tag = $name;
  391. }
  392. $n1 = empty($val['attr'])?'(\s*?)':'\s([^'.$end.']*)';
  393. $this->tempVar = array($tagLib, $tag);
  394. if (!$closeTag){
  395. $patterns = '/'.$begin.$parseTag.$n1.'\/(\s*?)'.$end.'/is';
  396. $content = preg_replace_callback($patterns, function($matches) use($tLib,$tag,$that){
  397. return $that->parseXmlTag($tLib,$tag,$matches[1],$matches[2]);
  398. },$content);
  399. }else{
  400. $patterns = '/'.$begin.$parseTag.$n1.$end.'(.*?)'.$begin.'\/'.$parseTag.'(\s*?)'.$end.'/is';
  401. for($i=0;$i<$level;$i++) {
  402. $content=preg_replace_callback($patterns,function($matches) use($tLib,$tag,$that){
  403. return $that->parseXmlTag($tLib,$tag,$matches[1],$matches[2]);
  404. },$content);
  405. }
  406. }
  407. }
  408. }
  409. }
  410. /**
  411. * 解析标签库的标签
  412. * 需要调用对应的标签库文件解析类
  413. * @access public
  414. * @param object $tagLib 标签库对象实例
  415. * @param string $tag 标签名
  416. * @param string $attr 标签属性
  417. * @param string $content 标签内容
  418. * @return string|false
  419. */
  420. public function parseXmlTag($tagLib,$tag,$attr,$content) {
  421. if(ini_get('magic_quotes_sybase'))
  422. $attr = str_replace('\"','\'',$attr);
  423. $parse = '_'.$tag;
  424. $content = trim($content);
  425. $tags = $tagLib->parseXmlAttr($attr,$tag);
  426. return $tagLib->$parse($tags,$content);
  427. }
  428. /**
  429. * 模板标签解析
  430. * 格式: {TagName:args [|content] }
  431. * @access public
  432. * @param string $tagStr 标签内容
  433. * @return string
  434. */
  435. public function parseTag($tagStr){
  436. if(is_array($tagStr)) $tagStr = $tagStr[2];
  437. //if (MAGIC_QUOTES_GPC) {
  438. $tagStr = stripslashes($tagStr);
  439. //}
  440. $flag = substr($tagStr,0,1);
  441. $flag2 = substr($tagStr,1,1);
  442. $name = substr($tagStr,1);
  443. if('$' == $flag && '.' != $flag2 && '(' != $flag2){ //解析模板变量 格式 {$varName}
  444. return $this->parseVar($name);
  445. }elseif('-' == $flag || '+'== $flag){ // 输出计算
  446. return '<?php echo '.$flag.$name.';?>';
  447. }elseif(':' == $flag){ // 输出某个函数的结果
  448. return '<?php echo '.$name.';?>';
  449. }elseif('~' == $flag){ // 执行某个函数
  450. return '<?php '.$name.';?>';
  451. }elseif(substr($tagStr,0,2)=='//' || (substr($tagStr,0,2)=='/*' && substr(rtrim($tagStr),-2)=='*/')){
  452. //注释标签
  453. return '';
  454. }
  455. // 未识别的标签直接返回
  456. return C('TMPL_L_DELIM') . $tagStr .C('TMPL_R_DELIM');
  457. }
  458. /**
  459. * 模板变量解析,支持使用函数
  460. * 格式: {$varname|function1|function2=arg1,arg2}
  461. * @access public
  462. * @param string $varStr 变量数据
  463. * @return string
  464. */
  465. public function parseVar($varStr){
  466. $varStr = trim($varStr);
  467. static $_varParseList = array();
  468. //如果已经解析过该变量字串,则直接返回变量值
  469. if(isset($_varParseList[$varStr])) return $_varParseList[$varStr];
  470. $parseStr = '';
  471. $varExists = true;
  472. if(!empty($varStr)){
  473. $varArray = explode('|',$varStr);
  474. //取得变量名称
  475. $var = array_shift($varArray);
  476. if('Think.' == substr($var,0,6)){
  477. // 所有以Think.打头的以特殊变量对待 无需模板赋值就可以输出
  478. $name = $this->parseThinkVar($var);
  479. }elseif( false !== strpos($var,'.')) {
  480. //支持 {$var.property}
  481. $vars = explode('.',$var);
  482. $var = array_shift($vars);
  483. switch(strtolower(C('TMPL_VAR_IDENTIFY'))) {
  484. case 'array': // 识别为数组
  485. $name = '$'.$var;
  486. foreach ($vars as $key=>$val)
  487. $name .= '["'.$val.'"]';
  488. break;
  489. case 'obj': // 识别为对象
  490. $name = '$'.$var;
  491. foreach ($vars as $key=>$val)
  492. $name .= '->'.$val;
  493. break;
  494. default: // 自动判断数组或对象 只支持二维
  495. $name = 'is_array($'.$var.')?$'.$var.'["'.$vars[0].'"]:$'.$var.'->'.$vars[0];
  496. }
  497. }elseif(false !== strpos($var,'[')) {
  498. //支持 {$var['key']} 方式输出数组
  499. $name = "$".$var;
  500. preg_match('/(.+?)\[(.+?)\]/is',$var,$match);
  501. $var = $match[1];
  502. }elseif(false !==strpos($var,':') && false ===strpos($var,'(') && false ===strpos($var,'::') && false ===strpos($var,'?')){
  503. //支持 {$var:property} 方式输出对象的属性
  504. $vars = explode(':',$var);
  505. $var = str_replace(':','->',$var);
  506. $name = "$".$var;
  507. $var = $vars[0];
  508. }else {
  509. $name = "$$var";
  510. }
  511. //对变量使用函数
  512. if(count($varArray)>0)
  513. $name = $this->parseVarFunction($name,$varArray);
  514. $parseStr = '<?php echo ('.$name.'); ?>';
  515. }
  516. $_varParseList[$varStr] = $parseStr;
  517. return $parseStr;
  518. }
  519. /**
  520. * 对模板变量使用函数
  521. * 格式 {$varname|function1|function2=arg1,arg2}
  522. * @access public
  523. * @param string $name 变量名
  524. * @param array $varArray 函数列表
  525. * @return string
  526. */
  527. public function parseVarFunction($name,$varArray){
  528. //对变量使用函数
  529. $length = count($varArray);
  530. //取得模板禁止使用函数列表
  531. $template_deny_funs = explode(',',C('TMPL_DENY_FUNC_LIST'));
  532. for($i=0;$i<$length ;$i++ ){
  533. $args = explode('=',$varArray[$i],2);
  534. //模板函数过滤
  535. $fun = trim($args[0]);
  536. switch($fun) {
  537. case 'default': // 特殊模板函数
  538. $name = '(isset('.$name.') && ('.$name.' !== ""))?('.$name.'):'.$args[1];
  539. break;
  540. default: // 通用模板函数
  541. if(!in_array($fun,$template_deny_funs)){
  542. if(isset($args[1])){
  543. if(strstr($args[1],'###')){
  544. $args[1] = str_replace('###',$name,$args[1]);
  545. $name = "$fun($args[1])";
  546. }else{
  547. $name = "$fun($name,$args[1])";
  548. }
  549. }else if(!empty($args[0])){
  550. $name = "$fun($name)";
  551. }
  552. }
  553. }
  554. }
  555. return $name;
  556. }
  557. /**
  558. * 特殊模板变量解析
  559. * 格式 以 $Think. 打头的变量属于特殊模板变量
  560. * @access public
  561. * @param string $varStr 变量字符串
  562. * @return string
  563. */
  564. public function parseThinkVar($varStr){
  565. $vars = explode('.',$varStr);
  566. $vars[1] = strtoupper(trim($vars[1]));
  567. $parseStr = '';
  568. if(count($vars)>=3){
  569. $vars[2] = trim($vars[2]);
  570. switch($vars[1]){
  571. case 'SERVER':
  572. $parseStr = '$_SERVER[\''.strtoupper($vars[2]).'\']';break;
  573. case 'GET':
  574. $parseStr = '$_GET[\''.$vars[2].'\']';break;
  575. case 'POST':
  576. $parseStr = '$_POST[\''.$vars[2].'\']';break;
  577. case 'COOKIE':
  578. if(isset($vars[3])) {
  579. $parseStr = '$_COOKIE[\''.$vars[2].'\'][\''.$vars[3].'\']';
  580. }else{
  581. $parseStr = 'cookie(\''.$vars[2].'\')';
  582. }
  583. break;
  584. case 'SESSION':
  585. if(isset($vars[3])) {
  586. $parseStr = '$_SESSION[\''.$vars[2].'\'][\''.$vars[3].'\']';
  587. }else{
  588. $parseStr = 'session(\''.$vars[2].'\')';
  589. }
  590. break;
  591. case 'ENV':
  592. $parseStr = '$_ENV[\''.strtoupper($vars[2]).'\']';break;
  593. case 'REQUEST':
  594. $parseStr = '$_REQUEST[\''.$vars[2].'\']';break;
  595. case 'CONST':
  596. $parseStr = strtoupper($vars[2]);break;
  597. case 'LANG':
  598. $parseStr = 'L("'.$vars[2].'")';break;
  599. case 'CONFIG':
  600. if(isset($vars[3])) {
  601. $vars[2] .= '.'.$vars[3];
  602. }
  603. $parseStr = 'C("'.$vars[2].'")';break;
  604. default:break;
  605. }
  606. }else if(count($vars)==2){
  607. switch($vars[1]){
  608. case 'NOW':
  609. $parseStr = "date('Y-m-d g:i a',time())";
  610. break;
  611. case 'VERSION':
  612. $parseStr = 'THINK_VERSION';
  613. break;
  614. case 'TEMPLATE':
  615. $parseStr = "'".$this->templateFile."'";//'C("TEMPLATE_NAME")';
  616. break;
  617. case 'LDELIM':
  618. $parseStr = 'C("TMPL_L_DELIM")';
  619. break;
  620. case 'RDELIM':
  621. $parseStr = 'C("TMPL_R_DELIM")';
  622. break;
  623. default:
  624. if(defined($vars[1]))
  625. $parseStr = $vars[1];
  626. }
  627. }
  628. return $parseStr;
  629. }
  630. /**
  631. * 加载公共模板并缓存 和当前模板在同一路径,否则使用相对路径
  632. * @access private
  633. * @param string $tmplPublicName 公共模板文件名
  634. * @param array $vars 要传递的变量列表
  635. * @return string
  636. */
  637. private function parseIncludeItem($tmplPublicName,$vars=array(),$extend){
  638. // 分析模板文件名并读取内容
  639. $parseStr = $this->parseTemplateName($tmplPublicName);
  640. // 替换变量
  641. foreach ($vars as $key=>$val) {
  642. $parseStr = str_replace('['.$key.']',$val,$parseStr);
  643. }
  644. // 再次对包含文件进行模板分析
  645. return $this->parseInclude($parseStr,$extend);
  646. }
  647. /**
  648. * 分析加载的模板文件并读取内容 支持多个模板文件读取
  649. * @access private
  650. * @param string $tmplPublicName 模板文件名
  651. * @return string
  652. */
  653. private function parseTemplateName($templateName){
  654. if(substr($templateName,0,1)=='$')
  655. //支持加载变量文件名
  656. $templateName = $this->get(substr($templateName,1));
  657. $array = explode(',',$templateName);
  658. $parseStr = '';
  659. foreach ($array as $templateName){
  660. if(empty($templateName)) continue;
  661. if(false === strpos($templateName,$this->config['template_suffix'])) {
  662. // 解析规则为 模块@主题/控制器/操作
  663. $templateName = T($templateName);
  664. }
  665. // 获取模板文件内容
  666. $parseStr .= file_get_contents($templateName);
  667. }
  668. return $parseStr;
  669. }
  670. }