国内流行的内容管理系统(CMS)多端全媒体解决方案 https://www.dedebiz.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1204 lines
42KB

  1. <?php
  2. if (!defined('DEDEINC')) exit('dedebiz');
  3. /**
  4. * 模板引擎文件
  5. *
  6. * @version $id:dedetemplate.class.php 3 15:44 2010年7月6日 tianya $
  7. * @package DedeBIZ.Libraries
  8. * @copyright Copyright (c) 2022 DedeBIZ.COM
  9. * @license https://www.dedebiz.com/license
  10. * @link https://www.dedebiz.com
  11. */
  12. function MakePublicTag($atts = array(), $refObj = '', $fields = array())
  13. {
  14. $atts['tagname'] = preg_replace("/[0-9]{1,}$/", "", $atts['tagname']);
  15. $plusfile = DEDEINC.'/tpllib/plus_'.$atts['tagname'].'.php';
  16. if (!file_exists($plusfile)) {
  17. if (isset($atts['rstype']) && $atts['rstype'] == 'string') {
  18. return '';
  19. } else {
  20. return array();
  21. }
  22. } else {
  23. include_once($plusfile);
  24. $func = 'plus_'.$atts['tagname'];
  25. return $func($atts, $refObj, $fields);
  26. }
  27. }
  28. /**
  29. * 设定属性的默认值
  30. *
  31. * @access public
  32. * @param array $atts 属性
  33. * @param array $attlist 属性列表
  34. * @return void
  35. */
  36. function FillAtts(&$atts, $attlist="")
  37. {
  38. $attlists = explode(',', $attlist);
  39. foreach ($attlists as $att) {
  40. list($k, $v) = explode('=', $att);
  41. if (!isset($atts[$k])) {
  42. $atts[$k] = $v;
  43. }
  44. }
  45. }
  46. /**
  47. * 把上级的fields传递给atts
  48. *
  49. * @access public
  50. * @param array $atts 属性
  51. * @param object $refObj 所属对象
  52. * @param array $fields 字段
  53. * @return string
  54. */
  55. function FillFields(&$atts, &$refObj, &$fields)
  56. {
  57. global $_vars;
  58. foreach ($atts as $k => $v) {
  59. if (preg_match('/^field\./i', $v)) {
  60. $key = preg_replace('/^field\./i', '', $v);
  61. if (isset($fields[$key])) {
  62. $atts[$k] = $fields[$key];
  63. }
  64. } else if (preg_match('/^var\./i', $v)) {
  65. $key = preg_replace('/^var\./i', '', $v);
  66. if (isset($_vars[$key])) {
  67. $atts[$k] = $_vars[$key];
  68. }
  69. } else if (preg_match('/^global\./i', $v)) {
  70. $key = preg_replace('/^global\./i', '', $v);
  71. if (isset($GLOBALS[$key])) {
  72. $atts[$k] = $GLOBALS[$key];
  73. }
  74. }
  75. }
  76. }
  77. /**
  78. * class Tag 标记的数据结构描述
  79. * function C__Tag();
  80. *
  81. * @package Tag
  82. * @subpackage DedeBIZ.Libraries
  83. * @link https://www.dedebiz.com
  84. */
  85. class Tag
  86. {
  87. var $isCompiler = FALSE; //标记是否已被替代,供解析器使用
  88. var $tagName = ""; //标记名称
  89. var $innerText = ""; //标记之间的文本
  90. var $startPos = 0; //标记起始位置
  91. var $endPos = 0; //标记结束位置
  92. var $cAtt; //标记属性描述,即是class TagAttribute
  93. var $tagValue = ""; //标记的值
  94. var $tagID = 0;
  95. /**
  96. * 获取标记的名称和值
  97. *
  98. * @access public
  99. * @return string
  100. */
  101. function GetName()
  102. {
  103. return strtolower($this->tagName);
  104. }
  105. function GetValue()
  106. {
  107. return $this->tagValue;
  108. }
  109. function IsAtt($str)
  110. {
  111. return $this->cAtt->IsAttribute($str);
  112. }
  113. function GetAtt($str)
  114. {
  115. return $this->cAtt->GetAtt($str);
  116. }
  117. /**
  118. * 获取底层模板
  119. *
  120. * @return string
  121. */
  122. function GetinnerText()
  123. {
  124. return $this->innerText;
  125. }
  126. }
  127. /**
  128. * 模板解析器
  129. * function C__DedeTemplate
  130. *
  131. * @package DedeTemplate
  132. * @subpackage DedeBIZ.Libraries
  133. * @link https://www.dedebiz.com
  134. */
  135. class DedeTemplate
  136. {
  137. var $tagMaxLen = 64;
  138. var $charToLow = TRUE;
  139. var $isCache = TRUE;
  140. var $isParse = FALSE;
  141. var $isCompiler = TRUE;
  142. var $templateDir = '';
  143. var $tempMkTime = 0;
  144. var $cacheFile = '';
  145. var $configFile = '';
  146. var $buildFile = '';
  147. var $refDir = '';
  148. var $cacheDir = '';
  149. var $templateFile = '';
  150. var $sourceString = '';
  151. var $cTags = array();
  152. var $count = -1;
  153. var $loopNum = 0;
  154. var $refObj = '';
  155. var $makeLoop = 0;
  156. var $tagStartWord = '{dede:';
  157. var $fullTagEndWord = '{/dede:';
  158. var $sTagEndWord = '/}';
  159. var $tagEndWord = '}';
  160. var $tpCfgs = array();
  161. /**
  162. * 析构函数
  163. *
  164. * @access public
  165. * @param string $templatedir 模板目录
  166. * @param string $refDir 所属目录
  167. * @return void
  168. */
  169. function __construct($templatedir = '', $refDir = '')
  170. {
  171. //缓存目录
  172. if ($templatedir == '') {
  173. $this->templateDir = DEDEROOT.'/templates';
  174. } else {
  175. $this->templateDir = $templatedir;
  176. }
  177. //模板include目录
  178. if ($refDir == '') {
  179. if (isset($GLOBALS['cfg_df_style'])) {
  180. $this->refDir = $this->templateDir.'/'.$GLOBALS['cfg_df_style'].'/';
  181. } else {
  182. $this->refDir = $this->templateDir;
  183. }
  184. }
  185. $this->cacheDir = DEDEROOT.$GLOBALS['cfg_tplcache_dir'];
  186. }
  187. //构造函数,兼容PHP4
  188. function DedeTemplate($templatedir = '', $refDir = '')
  189. {
  190. $this->__construct($templatedir, $refDir);
  191. }
  192. /**
  193. * 设定本类自身实例的类引用和使用本类的类实例(如果在类中使用本模板引擎,后一参数一般为$this)
  194. *
  195. * @access public
  196. * @param object $refObj 实例对象
  197. * @return string
  198. */
  199. function SetObject(&$refObj)
  200. {
  201. $this->refObj = $refObj;
  202. }
  203. /**
  204. * 设定Var的键值对
  205. *
  206. * @access public
  207. * @param string $k 键
  208. * @param string $v 值
  209. * @return string
  210. */
  211. function SetVar($k, $v)
  212. {
  213. $GLOBALS['_vars'][$k] = $v;
  214. }
  215. /**
  216. * 设定Var的键值对
  217. *
  218. * @access public
  219. * @param string $k 键
  220. * @param string $v 值
  221. * @return string
  222. */
  223. function Assign($k, $v)
  224. {
  225. $GLOBALS['_vars'][$k] = $v;
  226. }
  227. /**
  228. * 设定数组
  229. *
  230. * @access public
  231. * @param string $k 键
  232. * @param string $v 值
  233. * @return string
  234. */
  235. function SetArray($k, $v)
  236. {
  237. $GLOBALS[$k] = $v;
  238. }
  239. /**
  240. * 设置标记风格
  241. *
  242. * @access public
  243. * @param string $ts 标签开始标记
  244. * @param string $ftend 标签结束标记
  245. * @param string $stend 标签尾部结束标记
  246. * @param string $tend 结束标记
  247. * @return void
  248. */
  249. function SetTagStyle($ts = '{dede:', $ftend = '{/dede:', $stend = '/}', $tend = '}')
  250. {
  251. $this->tagStartWord = $ts;
  252. $this->fullTagEndWord = $ftend;
  253. $this->sTagEndWord = $stend;
  254. $this->tagEndWord = $tend;
  255. }
  256. /**
  257. * 获得模板设定的config值
  258. *
  259. * @access public
  260. * @param string $k 键名
  261. * @return string
  262. */
  263. function GetConfig($k)
  264. {
  265. return (isset($this->tpCfgs[$k]) ? $this->tpCfgs[$k] : '');
  266. }
  267. /**
  268. * 设定模板文件
  269. *
  270. * @access public
  271. * @param string $tmpfile 模板文件
  272. * @return void
  273. */
  274. function LoadTemplate($tmpfile)
  275. {
  276. if (!file_exists($tmpfile)) {
  277. echo " Template Not Found! ";
  278. exit();
  279. }
  280. $tmpfile = preg_replace("/[\\/]{1,}/", "/", $tmpfile);
  281. $tmpfiles = explode('/', $tmpfile);
  282. $tmpfileOnlyName = preg_replace("/(.*)\//", "", $tmpfile);
  283. $this->templateFile = $tmpfile;
  284. $this->refDir = '';
  285. for ($i = 0; $i < count($tmpfiles) - 1; $i++) {
  286. $this->refDir .= $tmpfiles[$i].'/';
  287. }
  288. if (!is_dir($this->cacheDir)) {
  289. $this->cacheDir = $this->refDir;
  290. }
  291. if ($this->cacheDir != '') {
  292. $this->cacheDir = $this->cacheDir.'/';
  293. }
  294. if (isset($GLOBALS['_DEBUG_CACHE'])) {
  295. $this->cacheDir = $this->refDir;
  296. }
  297. $this->cacheFile = $this->cacheDir.preg_replace("/\.(wml|html|htm|php)$/", "_".$this->GetEncodeStr($tmpfile).'.inc', $tmpfileOnlyName);
  298. $this->configFile = $this->cacheDir.preg_replace("/\.(wml|html|htm|php)$/", "_".$this->GetEncodeStr($tmpfile).'_config.inc', $tmpfileOnlyName);
  299. //不开启缓存、当缓存文件不存在、及模板为更新的文件的时候才载入模板并进行解析
  300. if (
  301. $this->isCache == FALSE || !file_exists($this->cacheFile)
  302. || filemtime($this->templateFile) > filemtime($this->cacheFile)
  303. ) {
  304. $t1 = ExecTime(); //debug
  305. $fp = fopen($this->templateFile, 'r');
  306. $this->sourceString = fread($fp, filesize($this->templateFile));
  307. fclose($fp);
  308. $this->ParseTemplate();
  309. //模板解析时间
  310. //echo ExecTime() - $t1;
  311. } else {
  312. //如果存在config文件,则载入此文件,该文件用于保存 $this->tpCfgs的内容,以供扩展用途
  313. //模板中用“{tag:config name='' value=''/}”来设定该值
  314. if (file_exists($this->configFile)) {
  315. include($this->configFile);
  316. }
  317. }
  318. }
  319. /**
  320. * 载入模板字符串
  321. *
  322. * @access public
  323. * @param string $str 模板字符串
  324. * @return void
  325. */
  326. function LoadString($str = '')
  327. {
  328. $this->sourceString = $str;
  329. $hashcode = md5($this->sourceString);
  330. $this->cacheFile = $this->cacheDir."/string_".$hashcode.".inc";
  331. $this->configFile = $this->cacheDir."/string_".$hashcode."_config.inc";
  332. $this->ParseTemplate();
  333. }
  334. /**
  335. * 调用此函数include一个编译后的PHP文件,通常是在最后一个步骤才调用本文件
  336. *
  337. * @access public
  338. * @return string
  339. */
  340. function CacheFile()
  341. {
  342. global $gtmpfile;
  343. $this->WriteCache();
  344. return $this->cacheFile;
  345. }
  346. /**
  347. * 显示内容,由于函数中会重新解压一次$GLOBALS变量,所以在动态页中,应该尽量少用本方法,
  348. * 取代之是直接在程序中 include $tpl->CacheFile(),不过include $tpl->CacheFile()这种方式不能在类或函数内使用
  349. *
  350. * @access public
  351. * @param string
  352. * @return void
  353. */
  354. function Display()
  355. {
  356. global $gtmpfile;
  357. extract($GLOBALS, EXTR_SKIP);
  358. $this->WriteCache();
  359. include $this->cacheFile;
  360. }
  361. /**
  362. * 保存运行后的程序为文件
  363. *
  364. * @access public
  365. * @param string $savefile 保存到的文件目录
  366. * @return void
  367. */
  368. function SaveTo($savefile)
  369. {
  370. extract($GLOBALS, EXTR_SKIP);
  371. $this->WriteCache();
  372. ob_start();
  373. include $this->cacheFile;
  374. $okstr = ob_get_contents();
  375. ob_end_clean();
  376. $fp = @fopen($savefile, "w") or die(" Tag Engine Create File FALSE! ");
  377. fwrite($fp, $okstr);
  378. fclose($fp);
  379. }
  380. /**
  381. * CheckDisabledFunctions
  382. *
  383. * COMMENT : CheckDisabledFunctions : 检查是否存在禁止的函数
  384. *
  385. * @access public
  386. * @param string
  387. * @return bool
  388. */
  389. function CheckDisabledFunctions($str, &$errmsg = '')
  390. {
  391. global $cfg_disable_funs;
  392. $cfg_disable_funs = isset($cfg_disable_funs) ? $cfg_disable_funs : 'phpinfo,eval,exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,file_put_contents,fsockopen,fopen,fwrite';
  393. //模板引擎增加disable_functions
  394. if (!defined('DEDEDISFUN')) {
  395. $tokens = token_get_all_nl($str);
  396. $disabled_functions = explode(',', $cfg_disable_funs);
  397. foreach ($tokens as $token) {
  398. if (is_array($token)) {
  399. if ($token[0] = '306' && in_array($token[1], $disabled_functions)) {
  400. $errmsg = 'DedeBIZ Error:function disabled "'.$token[1].'" <a href="https://www.dedebiz.com/help" target="_blank">more</a>';
  401. return FALSE;
  402. }
  403. }
  404. }
  405. }
  406. return TRUE;
  407. }
  408. /**
  409. * 解析模板并写缓存文件
  410. *
  411. * @access public
  412. * @param string $ctype 缓存类型
  413. * @return void
  414. */
  415. function WriteCache($ctype = 'all')
  416. {
  417. if (
  418. !file_exists($this->cacheFile) || $this->isCache == FALSE
  419. || (file_exists($this->templateFile) && (filemtime($this->templateFile) > filemtime($this->cacheFile)))
  420. ) {
  421. if (!$this->isParse) {
  422. $this->ParseTemplate();
  423. }
  424. $fp = fopen($this->cacheFile, 'w') or dir("Write Cache File Error! ");
  425. flock($fp, 3);
  426. $result = trim($this->GetResult());
  427. $errmsg = '';
  428. if (!$this->CheckDisabledFunctions($result, $errmsg)) {
  429. fclose($fp);
  430. @unlink($this->cacheFile);
  431. die($errmsg);
  432. }
  433. fwrite($fp, $result);
  434. fclose($fp);
  435. if (count($this->tpCfgs) > 0) {
  436. $fp = fopen($this->configFile, 'w') or dir("Write Config File Error! ");
  437. flock($fp, 3);
  438. fwrite($fp, '<'.'?php'."\r\n");
  439. foreach ($this->tpCfgs as $k => $v) {
  440. $v = str_replace("\"", "\\\"", $v);
  441. $v = str_replace("\$", "\\\$", $v);
  442. fwrite($fp, "\$this->tpCfgs['$k']=\"$v\";\r\n");
  443. }
  444. fwrite($fp, '?'.'>');
  445. fclose($fp);
  446. }
  447. }
  448. }
  449. /**
  450. * 获得模板文件名的md5字符串
  451. *
  452. * @access public
  453. * @param string $tmpfile 模板文件
  454. * @return string
  455. */
  456. function GetEncodeStr($tmpfile)
  457. {
  458. //$tmpfiles = explode('/',$tmpfile);
  459. $encodeStr = substr(md5($tmpfile), 0, 24);
  460. return $encodeStr;
  461. }
  462. /**
  463. * 解析模板
  464. *
  465. * @access public
  466. * @return void
  467. */
  468. function ParseTemplate()
  469. {
  470. if ($this->makeLoop > 5) {
  471. return;
  472. }
  473. $this->count = -1;
  474. $this->cTags = array();
  475. $this->isParse = TRUE;
  476. $sPos = 0;
  477. $ePos = 0;
  478. $tagStartWord = $this->tagStartWord;
  479. $fullTagEndWord = $this->fullTagEndWord;
  480. $sTagEndWord = $this->sTagEndWord;
  481. $tagEndWord = $this->tagEndWord;
  482. $startWordLen = strlen($tagStartWord);
  483. $sourceLen = strlen($this->sourceString);
  484. if ($sourceLen <= ($startWordLen + 3)) {
  485. return;
  486. }
  487. $cAtt = new TagAttributeParse();
  488. $cAtt->CharToLow = TRUE;
  489. //遍历模板字符串,请取标记及其属性信息
  490. $t = 0;
  491. $preTag = '';
  492. $tswLen = strlen($tagStartWord);
  493. for ($i = 0; $i < $sourceLen; $i++) {
  494. $ttagName = '';
  495. //如果不进行此判断,将无法识别相连的两个标记
  496. if ($i - 1 >= 0) {
  497. $ss = $i - 1;
  498. } else {
  499. $ss = 0;
  500. }
  501. $tagPos = strpos($this->sourceString, $tagStartWord, $ss);
  502. //判断后面是否还有模板标记
  503. if ($tagPos == 0 && ($sourceLen - $i < $tswLen
  504. || substr($this->sourceString, $i, $tswLen) != $tagStartWord)) {
  505. $tagPos = -1;
  506. break;
  507. }
  508. //获取TAG基本信息
  509. for ($j = $tagPos + $startWordLen; $j < $tagPos + $startWordLen + $this->tagMaxLen; $j++) {
  510. if (preg_match("/[ >\/\r\n\t\}\.]/", $this->sourceString[$j])) {
  511. break;
  512. } else {
  513. $ttagName .= $this->sourceString[$j];
  514. }
  515. }
  516. if ($ttagName != '') {
  517. $i = $tagPos + $startWordLen;
  518. $endPos = -1;
  519. //判断 '/}' '{tag:下一标记开始' '{/tag:标记结束' 谁最靠近
  520. $fullTagEndWordThis = $fullTagEndWord.$ttagName.$tagEndWord;
  521. $e1 = strpos($this->sourceString, $sTagEndWord, $i);
  522. $e2 = strpos($this->sourceString, $tagStartWord, $i);
  523. $e3 = strpos($this->sourceString, $fullTagEndWordThis, $i);
  524. $e1 = trim($e1);
  525. $e2 = trim($e2);
  526. $e3 = trim($e3);
  527. $e1 = ($e1 == '' ? '-1' : $e1);
  528. $e2 = ($e2 == '' ? '-1' : $e2);
  529. $e3 = ($e3 == '' ? '-1' : $e3);
  530. if ($e3 == -1) {
  531. //不存在'{/tag:标记'
  532. $endPos = $e1;
  533. $elen = $endPos + strlen($sTagEndWord);
  534. } else if ($e1 == -1) {
  535. //不存在 '/}'
  536. $endPos = $e3;
  537. $elen = $endPos + strlen($fullTagEndWordThis);
  538. }
  539. //同时存在 '/}' 和 '{/tag:标记'
  540. else {
  541. //如果 '/}' 比 '{tag:'、'{/tag:标记' 都要靠近,则认为结束标志是 '/}',否则结束标志为 '{/tag:标记'
  542. if ($e1 < $e2 && $e1 < $e3) {
  543. $endPos = $e1;
  544. $elen = $endPos + strlen($sTagEndWord);
  545. } else {
  546. $endPos = $e3;
  547. $elen = $endPos + strlen($fullTagEndWordThis);
  548. }
  549. }
  550. //如果找不到结束标记,则认为这个标记存在错误
  551. if ($endPos == -1) {
  552. echo "Tpl Character postion $tagPos, '$ttagName' Error<br>\r\n";
  553. break;
  554. }
  555. $i = $elen;
  556. //分析所找到的标记位置等信息
  557. $attStr = '';
  558. $innerText = '';
  559. $startInner = 0;
  560. for ($j = $tagPos + $startWordLen; $j < $endPos; $j++) {
  561. if ($startInner == 0) {
  562. if ($this->sourceString[$j] == $tagEndWord) {
  563. $startInner = 1;
  564. continue;
  565. } else {
  566. $attStr .= $this->sourceString[$j];
  567. }
  568. } else {
  569. $innerText .= $this->sourceString[$j];
  570. }
  571. }
  572. $ttagName = strtolower($ttagName);
  573. //if、php标记,把整个属性串视为属性
  574. if (preg_match("/^if[0-9]{0,}$/", $ttagName)) {
  575. $cAtt->cAttributes = new TagAttribute();
  576. $cAtt->cAttributes->count = 2;
  577. $cAtt->cAttributes->items['tagname'] = $ttagName;
  578. $cAtt->cAttributes->items['condition'] = preg_replace("/^if[0-9]{0,}[\r\n\t ]/", "", $attStr);
  579. $innerText = preg_replace("/\{else\}/i", '<'."?php\r\n}\r\nelse{\r\n".'?'.'>', $innerText);
  580. } else if ($ttagName == 'php') {
  581. $cAtt->cAttributes = new TagAttribute();
  582. $cAtt->cAttributes->count = 2;
  583. $cAtt->cAttributes->items['tagname'] = $ttagName;
  584. $cAtt->cAttributes->items['code'] = '<'."?php\r\n".trim(preg_replace(
  585. "/^php[0-9]{0,}[\r\n\t ]/",
  586. "",
  587. $attStr
  588. ))."\r\n?".'>';
  589. } else {
  590. //普通标记,解释属性
  591. $cAtt->SetSource($attStr);
  592. }
  593. $this->count++;
  594. $cTag = new Tag();
  595. $cTag->tagName = $ttagName;
  596. $cTag->startPos = $tagPos;
  597. $cTag->endPos = $i;
  598. $cTag->cAtt = $cAtt->cAttributes;
  599. $cTag->isCompiler = FALSE;
  600. $cTag->tagID = $this->count;
  601. $cTag->innerText = $innerText;
  602. $this->cTags[$this->count] = $cTag;
  603. } else {
  604. $i = $tagPos + $startWordLen;
  605. break;
  606. }
  607. }//结束遍历模板字符串
  608. if ($this->count > -1 && $this->isCompiler) {
  609. $this->CompilerAll();
  610. }
  611. }
  612. /**
  613. * 把模板标记转换为PHP代码
  614. *
  615. * @access public
  616. * @return void
  617. */
  618. function CompilerAll()
  619. {
  620. $this->loopNum++;
  621. if ($this->loopNum > 10) {
  622. return;//限制最大递归深度为 10 以防止因标记出错等可能性导致死循环
  623. }
  624. $ResultString = '';
  625. $nextTagEnd = 0;
  626. for ($i = 0; isset($this->cTags[$i]); $i++) {
  627. $ResultString .= substr($this->sourceString, $nextTagEnd, $this->cTags[$i]->startPos - $nextTagEnd);
  628. $ResultString .= $this->CompilerOneTag($this->cTags[$i]);
  629. $nextTagEnd = $this->cTags[$i]->endPos;
  630. }
  631. $slen = strlen($this->sourceString);
  632. if ($slen > $nextTagEnd) {
  633. $ResultString .= substr($this->sourceString, $nextTagEnd, $slen - $nextTagEnd);
  634. }
  635. $this->sourceString = $ResultString;
  636. $this->ParseTemplate();
  637. }
  638. /**
  639. * 获得最终结果
  640. *
  641. * @access public
  642. * @return string
  643. */
  644. function GetResult()
  645. {
  646. if (!$this->isParse) {
  647. $this->ParseTemplate();
  648. }
  649. $addset = '';
  650. $addset .= '<'.'?php'."\r\n".'if (!isset($GLOBALS[\'_vars\'])) $GLOBALS[\'_vars\'] = array(); '."\r\n".'$fields = array();'."\r\n".'?'.'>';
  651. return preg_replace("/\?".">[ \r\n\t]{0,}<"."\?php/", "", $addset.$this->sourceString);
  652. }
  653. /**
  654. * 编译单个标记
  655. *
  656. * @access public
  657. * @param object $cTag 标签
  658. * @return string
  659. */
  660. function CompilerOneTag(&$cTag)
  661. {
  662. $cTag->isCompiler = TRUE;
  663. $tagname = $cTag->tagName;
  664. $varname = $cTag->GetAtt('name');
  665. $rsvalue = "";
  666. //用于在模板中设置一个变量以提供作扩展用途
  667. //此变量直接提交到 this->tpCfgs 中,并会生成与模板对应的缓存文件 ***_config.php 文件
  668. if ($tagname == 'config') {
  669. $this->tpCfgs[$varname] = $cTag->GetAtt('value');
  670. } else if ($tagname == 'global') {
  671. $cTag->tagValue = $this->CompilerArrayVar('global', $varname);
  672. if ($cTag->GetAtt('function') != '') {
  673. $cTag->tagValue = $this->CompilerFunction($cTag->GetAtt('function'), $cTag->tagValue);
  674. }
  675. $cTag->tagValue = '<'.'?php echo '.$cTag->tagValue.'; ?'.'>';
  676. } else if ($tagname == 'cfg') {
  677. $cTag->tagValue = '$GLOBALS[\'cfg_'.$varname.'\']'; //处理函数
  678. if ($cTag->GetAtt('function') != '') {
  679. $cTag->tagValue = $this->CompilerFunction($cTag->GetAtt('function'), $cTag->tagValue);
  680. }
  681. $cTag->tagValue = '<'.'?php echo '.$cTag->tagValue.'; ?'.'>';
  682. } else if ($tagname == 'name') {
  683. $cTag->tagValue = '$'.$varname; //处理函数
  684. if ($cTag->GetAtt('function') != '') {
  685. $cTag->tagValue = $this->CompilerFunction($cTag->GetAtt('function'), $cTag->tagValue);
  686. }
  687. $cTag->tagValue = '<'.'?php echo '.$cTag->tagValue.'; ?'.'>';
  688. } else if ($tagname == 'object') {
  689. list($_obs, $_em) = explode('->', $varname);
  690. $cTag->tagValue = "\$GLOBALS['{$_obs}']->{$_em}"; //处理函数
  691. if ($cTag->GetAtt('function') != '') {
  692. $cTag->tagValue = $this->CompilerFunction($cTag->GetAtt('function'), $cTag->tagValue);
  693. }
  694. $cTag->tagValue = '<'.'?php echo '.$cTag->tagValue.'; ?'.'>';
  695. } else if ($tagname == 'var') {
  696. $cTag->tagValue = $this->CompilerArrayVar('var', $varname);
  697. if ($cTag->GetAtt('function') != '') {
  698. $cTag->tagValue = $this->CompilerFunction($cTag->GetAtt('function'), $cTag->tagValue);
  699. }
  700. //增加默认空值处理
  701. if ($cTag->GetAtt('default') != '') {
  702. $cTag->tagValue = '<'.'?php echo empty('.$cTag->tagValue.')? \''.addslashes($cTag->GetAtt('default')).'\':'.$cTag->tagValue.'; ?'.'>';
  703. } else {
  704. $cTag->tagValue = '<'.'?php echo '.$cTag->tagValue.'; ?'.'>';
  705. }
  706. } else if ($tagname == 'field') {
  707. $cTag->tagValue = '$fields[\''.$varname.'\']';
  708. if ($cTag->GetAtt('function') != '') {
  709. $cTag->tagValue = $this->CompilerFunction($cTag->GetAtt('function'), $cTag->tagValue);
  710. }
  711. $cTag->tagValue = '<'.'?php echo '.$cTag->tagValue.'; ?'.'>';
  712. } else if (preg_match("/^key[0-9]{0,}/", $tagname) || preg_match("/^value[0-9]{0,}/", $tagname)) {
  713. if (preg_match("/^value[0-9]{0,}/", $tagname) && $varname != '') {
  714. $cTag->tagValue = '<'.'?php echo '.$this->CompilerArrayVar($tagname, $varname).'; ?'.'>';
  715. } else {
  716. $cTag->tagValue = '<'.'?php echo $'.$tagname.'; ?'.'>';
  717. }
  718. } else if (preg_match("/^if[0-9]{0,}$/", $tagname)) {
  719. $cTag->tagValue = $this->Compilerif ($cTag);
  720. } else if ($tagname == 'echo') {
  721. if (trim($cTag->GetInnerText()) == '') $cTag->tagValue = $cTag->GetAtt('code');
  722. else {
  723. $cTag->tagValue = '<'."?php echo $".trim($cTag->GetInnerText())." ;?".'>';
  724. }
  725. } else if ($tagname == 'php') {
  726. if (trim($cTag->GetInnerText()) == '') $cTag->tagValue = $cTag->GetAtt('code');
  727. else {
  728. $cTag->tagValue = '<'."?php\r\n".trim($cTag->GetInnerText())."\r\n?".'>';
  729. }
  730. }
  731. //遍历数组
  732. else if (preg_match("/^array[0-9]{0,}/", $tagname)) {
  733. $kk = '$key';
  734. $vv = '$value';
  735. if ($cTag->GetAtt('key') != '') {
  736. $kk = '$key'.$cTag->GetAtt('key');
  737. }
  738. if ($cTag->GetAtt('value') != '') {
  739. $vv = '$value'.$cTag->GetAtt('value');
  740. }
  741. $addvar = '';
  742. if (!preg_match("/\(/", $varname)) {
  743. $varname = '$GLOBALS[\''.$varname.'\']';
  744. } else {
  745. $addvar = "\r\n".'$myarrs = $pageClass->'.$varname.";\r\n";
  746. $varname = ' $myarrs ';
  747. }
  748. $rsvalue = '<'.'?php '.$addvar.' foreach('.$varname.' as '.$kk.'=>'.$vv.'){ ?'.">";
  749. $rsvalue .= $cTag->GetInnerText();
  750. $rsvalue .= '<'.'?php } ?'.">\r\n";
  751. $cTag->tagValue = $rsvalue;
  752. }
  753. //system文件
  754. else if ($tagname == 'include') {
  755. $filename = $cTag->GetAtt('file');
  756. if ($filename == '') {
  757. $filename = $cTag->GetAtt('filename');
  758. }
  759. $cTag->tagValue = $this->CompilerInclude($filename, FALSE);
  760. if ($cTag->tagValue == 0) $cTag->tagValue = '';
  761. $cTag->tagValue = '<'.'?php include $this->CompilerInclude("'.$filename.'");'."\r\n".' ?'.'>';
  762. } else if ($tagname == 'label') {
  763. $bindFunc = $cTag->GetAtt('bind');
  764. $rsvalue = 'echo '.$bindFunc.";\r\n";
  765. $rsvalue = '<'.'?php '.$rsvalue.' ?'.">\r\n";
  766. $cTag->tagValue = $rsvalue;
  767. } else if ($tagname == 'datalist') {
  768. //生成属性数组
  769. foreach ($cTag->cAtt->items as $k => $v) {
  770. $v = $this->TrimAtts($v);
  771. $rsvalue .= '$atts[\''.$k.'\'] = \''.str_replace("'", "\\'", $v)."';\r\n";
  772. }
  773. $rsvalue = '<'.'?php'."\r\n".'$atts = array();'."\r\n".$rsvalue;
  774. $rsvalue .= '$blockValue = $this->refObj->GetArcList($atts,$this->refObj,$fields); '."\r\n";
  775. $rsvalue .= '$total = $this->refObj->totalResult;'."\r\n";
  776. $rsvalue .= 'if (is_array($blockValue)){'."\r\n";
  777. $rsvalue .= 'foreach( $blockValue as $key=>$fields )'."\r\n{\r\n".'?'.">";
  778. $rsvalue .= $cTag->GetInnerText();
  779. $rsvalue .= '<'.'?php'."\r\n}if (\$total==0 && !empty(\$atts['empty'])) echo \$atts['empty'];\r\n}".'?'.'>';
  780. $cTag->tagValue = $rsvalue;
  781. } else if ($tagname == 'pagelist') {
  782. //生成属性数组
  783. foreach ($cTag->cAtt->items as $k => $v) {
  784. $v = $this->TrimAtts($v);
  785. $rsvalue .= '$atts[\''.$k.'\'] = \''.str_replace("'", "\\'", $v)."';\r\n";
  786. }
  787. $rsvalue = '<'.'?php'."\r\n".'$atts = array();'."\r\n".$rsvalue;
  788. $rsvalue .= ' echo $this->refObj->GetPageList($atts,$this->refObj,$fields); '."\r\n".'?'.">\r\n";
  789. $cTag->tagValue = $rsvalue;
  790. } else {
  791. $bindFunc = $cTag->GetAtt('bind');
  792. $bindType = $cTag->GetAtt('bindtype');
  793. $rstype = ($cTag->GetAtt('resulttype') == '' ? $cTag->GetAtt('rstype') : $cTag->GetAtt('resulttype'));
  794. $rstype = strtolower($rstype);
  795. //生成属性数组
  796. foreach ($cTag->cAtt->items as $k => $v) {
  797. if (preg_match("/(bind|bindtype)/i", $k)) {
  798. continue;
  799. }
  800. $v = $this->TrimAtts($v);
  801. $rsvalue .= '$atts[\''.$k.'\'] = \''.str_replace("'", "\\'", $v)."';\r\n";
  802. }
  803. $rsvalue = '<'.'?php'."\r\n".'$atts = array();'."\r\n".$rsvalue;
  804. //绑定到默认函数还是指定函数(datasource属性指定)
  805. if ($bindFunc == '') {
  806. $rsvalue .= '$blockValue = MakePublicTag($atts,$this->refObj,$fields); '."\r\n";
  807. } else {
  808. //自定义绑定函数如果不指定 bindtype,则指向$this->refObj->绑定函数名,即是默认指向被引用的类对象
  809. if ($bindType == '') $rsvalue .= '$blockValue = $this->refObj->'.$bindFunc.'($atts,$this->refObj,$fields); '."\r\n";
  810. else $rsvalue .= '$blockValue = '.$bindFunc.'($atts,$this->refObj,$fields); '."\r\n";
  811. }
  812. //返回结果类型:默认为 array 是一个二维数组,string 是字符串
  813. if ($rstype == 'string') {
  814. $rsvalue .= 'echo $blockValue;'."\r\n".'?'.">";
  815. } else {
  816. $rsvalue .= 'if (is_array($blockValue) && count($blockValue) > 0){'."\r\n";
  817. $rsvalue .= 'foreach( $blockValue as $key=>$fields )'."\r\n{\r\n".'?'.">";
  818. $rsvalue .= $cTag->GetInnerText();
  819. $rsvalue .= '<'.'?php'."\r\n}\r\n}\r\n".'?'.'>';
  820. }
  821. $cTag->tagValue = $rsvalue;
  822. }
  823. return $cTag->tagValue;
  824. }
  825. /**
  826. * 编译可能为数组的变量
  827. *
  828. * @access public
  829. * @param string $vartype 变量类型
  830. * @param string $varname 变量名称
  831. * @return string
  832. */
  833. function CompilerArrayVar($vartype, $varname)
  834. {
  835. $okvalue = '';
  836. if (!preg_match("/\[/", $varname)) {
  837. if (preg_match("/^value/", $vartype)) {
  838. $varname = $vartype.'.'.$varname;
  839. }
  840. $varnames = explode('.', $varname);
  841. if (isset($varnames[1])) {
  842. $varname = $varnames[0];
  843. for ($i = 1; isset($varnames[$i]); $i++) {
  844. $varname .= "['".$varnames[$i]."']";
  845. }
  846. }
  847. }
  848. if (preg_match("/\[/", $varname)) {
  849. $varnames = explode('[', $varname);
  850. $arrend = '';
  851. for ($i = 1; isset($varnames[$i]); $i++) {
  852. $arrend .= '['.$varnames[$i];
  853. }
  854. if (!preg_match("/[\"']/", $arrend)) {
  855. $arrend = str_replace('[', '', $arrend);
  856. $arrend = str_replace(']', '', $arrend);
  857. $arrend = "['{$arrend}']";
  858. }
  859. if ($vartype == 'var') {
  860. $okvalue = '$GLOBALS[\'_vars\'][\''.$varnames[0].'\']'.$arrend;
  861. } else if (preg_match("/^value/", $vartype)) {
  862. $okvalue = '$'.$varnames[0].$arrend;
  863. } else if ($vartype == 'field') {
  864. $okvalue = '$fields[\''.$varnames[0].'\']'.$arrend;
  865. } else {
  866. $okvalue = '$GLOBALS[\''.$varnames[0].'\']'.$arrend;
  867. }
  868. } else {
  869. if ($vartype == 'var') {
  870. $okvalue = '$GLOBALS[\'_vars\'][\''.$varname.'\']';
  871. } else if (preg_match("/^value/", $vartype)) {
  872. $okvalue = '$'.$vartype;
  873. } else if ($vartype == 'field') {
  874. $okvalue = '$fields[\''.trim($varname).'\']';
  875. } else {
  876. $okvalue = '$GLOBALS[\''.$varname.'\']';
  877. }
  878. }
  879. return $okvalue;
  880. }
  881. /**
  882. * 编译if标记
  883. *
  884. * @access public
  885. * @param string $cTag 标签
  886. * @return string
  887. */
  888. function Compilerif ($cTag)
  889. {
  890. $condition = trim($cTag->GetAtt('condition'));
  891. if ($condition == '') {
  892. $cTag->tagValue = '';
  893. return '';
  894. }
  895. if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
  896. $condition = preg_replace_callback("/((var\.|field\.|cfg\.|global\.|key[0-9]{0,}\.|value[0-9]{0,}\.)[\._a-z0-9]+)/is", "private_rt", $condition);
  897. } else {
  898. $condition = preg_replace("/((var\.|field\.|cfg\.|global\.|key[0-9]{0,}\.|value[0-9]{0,}\.)[\._a-z0-9]+)/ies", "private_rt('\\1')", $condition);
  899. }
  900. $rsvalue = '<'.'?php if ('.$condition.'){ ?'.'>';
  901. $rsvalue .= $cTag->GetInnerText();
  902. $rsvalue .= '<'.'?php } ?'.'>';
  903. return $rsvalue;
  904. }
  905. /**
  906. * 处理block区块传递的atts属性的值
  907. *
  908. * @access public
  909. * @param string $v 值
  910. * @return string
  911. */
  912. function TrimAtts($v)
  913. {
  914. $v = str_replace('<'.'?', '&lt;?', $v);
  915. $v = str_replace('?'.'>', '?&gt;', $v);
  916. return $v;
  917. }
  918. /**
  919. * 函数 function 语法处理
  920. *
  921. * @access public
  922. * @param string $funcstr 函数字符串
  923. * @param string $nvalue 函数值
  924. * @return string
  925. */
  926. function CompilerFunction($funcstr, $nvalue)
  927. {
  928. $funcstr = str_replace('@quote', '"', $funcstr);
  929. $funcstr = str_replace('@me', $nvalue, $funcstr);
  930. return $funcstr;
  931. }
  932. /**
  933. * 引入文件system语法处理
  934. *
  935. * @access public
  936. * @param string $filename 文件名
  937. * @param string $isload 是否载入
  938. * @return string
  939. */
  940. function CompilerInclude($filename, $isload = TRUE)
  941. {
  942. $okfile = '';
  943. if (@file_exists($filename)) {
  944. $okfile = $filename;
  945. } else if (@file_exists($this->refDir.$filename)) {
  946. $okfile = $this->refDir.$filename;
  947. } else if (@file_exists($this->refDir."../".$filename)) {
  948. $okfile = $this->refDir."../".$filename;
  949. }
  950. if ($okfile == '') return 0;
  951. if (!$isload) return 1;
  952. $itpl = new DedeTemplate($this->templateDir);
  953. $itpl->isCache = $this->isCache;
  954. $itpl->SetObject($this->refObj);
  955. $itpl->LoadTemplate($okfile);
  956. return $itpl->CacheFile();
  957. }
  958. }
  959. /**
  960. * class TagAttribute Tag属性集合
  961. * function C__TagAttribute();
  962. * 属性的数据描述
  963. *
  964. * @package TagAttribute
  965. * @subpackage DedeBIZ.Libraries
  966. * @link https://www.dedebiz.com
  967. */
  968. class TagAttribute
  969. {
  970. var $count = -1;
  971. var $items = array(); //属性元素的集合
  972. /**
  973. * 获得某个属性
  974. *
  975. * @access public
  976. * @param string $str 预处理字符串
  977. * @return string
  978. */
  979. function GetAtt($str)
  980. {
  981. if ($str == "") {
  982. return "";
  983. }
  984. if (isset($this->items[$str])) {
  985. return $this->items[$str];
  986. } else {
  987. return "";
  988. }
  989. }
  990. /**
  991. * 同上
  992. *
  993. * @access public
  994. * @param string $str 预处理字符串
  995. * @return string
  996. */
  997. function GetAttribute($str)
  998. {
  999. return $this->GetAtt($str);
  1000. }
  1001. /**
  1002. * 判断属性是否存在
  1003. *
  1004. * @access public
  1005. * @param string $str 预处理字符串
  1006. * @return bool
  1007. */
  1008. function IsAttribute($str)
  1009. {
  1010. if (isset($this->items[$str])) return TRUE;
  1011. else return FALSE;
  1012. }
  1013. /**
  1014. * 获得标记名称
  1015. *
  1016. * @access public
  1017. * @return string
  1018. */
  1019. function GettagName()
  1020. {
  1021. return $this->GetAtt("tagname");
  1022. }
  1023. /**
  1024. * 获得属性个数
  1025. *
  1026. * @access public
  1027. * @return int
  1028. */
  1029. function Getcount()
  1030. {
  1031. return $this->count + 1;
  1032. }
  1033. }//End Class
  1034. /**
  1035. * 属性解析器
  1036. * function C__TagAttributeParse();
  1037. *
  1038. * @package TagAttribute
  1039. * @subpackage DedeBIZ.Libraries
  1040. * @link https://www.dedebiz.com
  1041. */
  1042. class TagAttributeParse
  1043. {
  1044. var $sourceString = "";
  1045. var $sourceMaxSize = 1024;
  1046. var $cAttributes;
  1047. var $charToLow = TRUE;
  1048. function SetSource($str = "")
  1049. {
  1050. $this->cAttributes = new TagAttribute();
  1051. $strLen = 0;
  1052. $this->sourceString = trim(preg_replace("/[ \r\n\t\f]{1,}/", " ", $str));
  1053. $strLen = strlen($this->sourceString);
  1054. if ($strLen > 0 && $strLen <= $this->sourceMaxSize) {
  1055. $this->ParseAttribute();
  1056. }
  1057. }
  1058. /**
  1059. * 解析属性
  1060. *
  1061. * @access public
  1062. * @return void
  1063. */
  1064. function ParseAttribute()
  1065. {
  1066. $d = '';
  1067. $tmpatt = '';
  1068. $tmpvalue = '';
  1069. $startdd = -1;
  1070. $ddtag = '';
  1071. $hasAttribute = FALSE;
  1072. $strLen = strlen($this->sourceString);
  1073. $this->cAttributes->items = array();
  1074. //获得Tag的名称,解析到 cAtt->GetAtt('tagname') 中
  1075. for ($i = 0; $i < $strLen; $i++) {
  1076. if ($this->sourceString[$i] == ' ') {
  1077. $this->cAttributes->count++;
  1078. $tmpvalues = explode('.', $tmpvalue);
  1079. $this->cAttributes->items['tagname'] = ($this->charToLow ? strtolower($tmpvalues[0]) : $tmpvalues[0]);
  1080. if (isset($tmpvalues[2])) {
  1081. $okname = $tmpvalues[1];
  1082. for ($j = 2; isset($tmpvalues[$j]); $j++) {
  1083. $okname .= "['".$tmpvalues[$j]."']";
  1084. }
  1085. $this->cAttributes->items['name'] = $okname;
  1086. } else if (isset($tmpvalues[1]) && $tmpvalues[1] != '') {
  1087. $this->cAttributes->items['name'] = $tmpvalues[1];
  1088. }
  1089. $tmpvalue = '';
  1090. $hasAttribute = TRUE;
  1091. break;
  1092. } else {
  1093. $tmpvalue .= $this->sourceString[$i];
  1094. }
  1095. }
  1096. //不存在属性列表的情况
  1097. if (!$hasAttribute) {
  1098. $this->cAttributes->count++;
  1099. $tmpvalues = explode('.', $tmpvalue);
  1100. $this->cAttributes->items['tagname'] = ($this->charToLow ? strtolower($tmpvalues[0]) : $tmpvalues[0]);
  1101. if (isset($tmpvalues[2])) {
  1102. $okname = $tmpvalues[1];
  1103. for ($i = 2; isset($tmpvalues[$i]); $i++) {
  1104. $okname .= "['".$tmpvalues[$i]."']";
  1105. }
  1106. $this->cAttributes->items['name'] = $okname;
  1107. } else if (isset($tmpvalues[1]) && $tmpvalues[1] != '') {
  1108. $this->cAttributes->items['name'] = $tmpvalues[1];
  1109. }
  1110. return;
  1111. }
  1112. $tmpvalue = '';
  1113. //如果字符串含有属性值,遍历源字符串,并获得各属性
  1114. for ($i; $i < $strLen; $i++) {
  1115. $d = $this->sourceString[$i];
  1116. //查找属性名称
  1117. if ($startdd == -1) {
  1118. if ($d != '=') {
  1119. $tmpatt .= $d;
  1120. } else {
  1121. if ($this->charToLow) {
  1122. $tmpatt = strtolower(trim($tmpatt));
  1123. } else {
  1124. $tmpatt = trim($tmpatt);
  1125. }
  1126. $startdd = 0;
  1127. }
  1128. }
  1129. //查找属性的限定标志
  1130. else if ($startdd == 0) {
  1131. switch ($d) {
  1132. case ' ':
  1133. break;
  1134. case '\'':
  1135. $ddtag = '\'';
  1136. $startdd = 1;
  1137. break;
  1138. case '"':
  1139. $ddtag = '"';
  1140. $startdd = 1;
  1141. break;
  1142. default:
  1143. $tmpvalue .= $d;
  1144. $ddtag = ' ';
  1145. $startdd = 1;
  1146. break;
  1147. }
  1148. } else if ($startdd == 1) {
  1149. if ($d == $ddtag && (isset($this->sourceString[$i - 1]) && $this->sourceString[$i - 1] != "\\")) {
  1150. $this->cAttributes->count++;
  1151. $this->cAttributes->items[$tmpatt] = trim($tmpvalue);
  1152. $tmpatt = '';
  1153. $tmpvalue = '';
  1154. $startdd = -1;
  1155. } else {
  1156. $tmpvalue .= $d;
  1157. }
  1158. }
  1159. } //for
  1160. //最后一个属性的给值
  1161. if ($tmpatt != '') {
  1162. $this->cAttributes->count++;
  1163. $this->cAttributes->items[$tmpatt] = trim($tmpvalue);
  1164. } //print_r($this->cAttributes->items);
  1165. } //end func
  1166. } //End Class
  1167. /**
  1168. * 私有标签编译,主要用于if标签内的字符串解析
  1169. *
  1170. * @access public
  1171. * @param string $str 需要编译的字符串
  1172. * @return string
  1173. */
  1174. function private_rt($str)
  1175. {
  1176. if (is_array($str)) {
  1177. $arr = explode('.', $str[0]);
  1178. } else {
  1179. $arr = explode('.', $str);
  1180. }
  1181. $rs = '$GLOBALS[\'';
  1182. if ($arr[0] == 'cfg') {
  1183. return $rs.'cfg_'.$arr[1]."']";
  1184. } elseif ($arr[0] == 'var') {
  1185. $arr[0] = '_vars';
  1186. $rs .= implode('\'][\'', $arr);
  1187. $rs .= "']";
  1188. return $rs;
  1189. } elseif ($arr[0] == 'global') {
  1190. unset($arr[0]);
  1191. $rs .= implode('\'][\'', $arr);
  1192. $rs .= "']";
  1193. return $rs;
  1194. } else {
  1195. if ($arr[0] == 'field') $arr[0] = 'fields';
  1196. $rs = '$'.$arr[0]."['";
  1197. unset($arr[0]);
  1198. $rs .= implode('\'][\'', $arr);
  1199. $rs .= "']";
  1200. return $rs;
  1201. }
  1202. }
  1203. ?>