Template.class.php 32 KB

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