class_template.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. <?php
  2. /**
  3. * [Discuz!] (C)2001-2099 Discuz! Team
  4. * This is NOT a freeware, use is subject to license terms
  5. * https://license.discuz.vip
  6. */
  7. if (!defined('IN_DISCUZ')) {
  8. exit('Access Denied');
  9. }
  10. class template {
  11. var $subtemplates = [];
  12. var $csscurmodules = '';
  13. var $replacecode = ['search' => [], 'replace' => []];
  14. var $blocks = [];
  15. var $language = [];
  16. var $file = '';
  17. var $filetype = 'htm';
  18. var $debug = 0;
  19. var $cellFuncs = [];
  20. function parse_template($tplfile, $templateid = 1, $tpldir = '', $file = '', $cachefile = '', $postparse = null) {
  21. $f = $tplfile;
  22. $basefile = basename($f, '.' . $this->filetype);
  23. $file == 'common/header' && defined('CURMODULE') && CURMODULE && $file = 'common/header_' . CURMODULE;
  24. $this->file = $file;
  25. if (tplfile::file_exists($tplfile)) {
  26. $template = tplfile::file_get_contents($tplfile);
  27. } elseif (tplfile::file_exists($filename = substr($tplfile, 0, -(strlen($this->filetype) + 1)) . '.php')) {
  28. $template = tplfile::file_get_contents($filename);
  29. $template = tplfile::getphptemplate($template);
  30. } else {
  31. $tpl = $tpldir . '/' . $file . '.' . $this->filetype;
  32. $tplfile = $tplfile != $tpl ? $tpl . ', ' . $tplfile : $tplfile;
  33. $this->error('template_notfound', $tplfile);
  34. }
  35. if ($this->debug) {
  36. $template = $this->insertdebugmsg($template, $tplfile);
  37. }
  38. $var_regexp = "((\\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(\-\>)?[a-zA-Z0-9_\x7f-\xff]*)(\[[a-zA-Z0-9_\-\.\"\'\[\]\$\x7f-\xff]+\])*)";
  39. $const_regexp = "([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)";
  40. $headerexists = preg_match('/{(sub)?template\s+[\w:\/]+?header\}/', $template);
  41. $this->subtemplates = [];
  42. for ($i = 1; $i <= 3; $i++) {
  43. if (strexists($template, '{subtemplate')) {
  44. $template = preg_replace_callback("/[\n\r\t]*(\<\!\-\-)?\{subtemplate\s+([a-z0-9_:\/]+)\}(\-\-\>)?[\n\r\t]*/is", [$this, 'parse_template_callback_loadsubtemplate_2'], $template);
  45. }
  46. }
  47. $template = preg_replace("/([\n\r]+)\t+/s", "\\1", $template);
  48. $template = preg_replace('/\/\*\*\{(.+?)\}\*\//s', "{\\1}", $template);
  49. $template = preg_replace('/\<\!\-\-\{(.+?)\}\-\-\>/s', "{\\1}", $template);
  50. $template = preg_replace_callback("/[\n\r\t]*\{block\/(\d+?)\}[\n\r\t]*/i", [$this, 'parse_template_callback_blocktags_1'], $template);
  51. $template = preg_replace_callback("/[\n\r\t]*\{blockdata\/(\d+?)\}[\n\r\t]*/i", [$this, 'parse_template_callback_blockdatatags_1'], $template);
  52. $template = preg_replace_callback("/[\n\r\t]*\{ad\/(.+?)\}[\n\r\t]*/i", [$this, 'parse_template_callback_adtags_1'], $template);
  53. $template = preg_replace_callback("/[\n\r\t]*\{ad\s+([a-zA-Z0-9_\[\]]+)\/(.+?)\}[\n\r\t]*/i", [$this, 'parse_template_callback_adtags_21'], $template);
  54. $template = preg_replace_callback("/[\n\r\t]*\{date\((.+?)\)\}[\n\r\t]*/i", [$this, 'parse_template_callback_datetags_1'], $template);
  55. $template = preg_replace_callback("/[\n\r\t]*\{avatar\((.+?)\)\}[\n\r\t]*/i", [$this, 'parse_template_callback_avatartags_1'], $template);
  56. $template = preg_replace_callback("/[\n\r\t]*\{eval\}\s*(\<\!\-\-)*(.+?)(\-\-\>)*\s*\{\/eval\}[\n\r\t]*/is", [$this, 'parse_template_callback_evaltags_2'], $template);
  57. $template = preg_replace_callback("/[\n\r\t]*\{eval\s+(.+?)\s*\}[\n\r\t]*/is", [$this, 'parse_template_callback_evaltags_1'], $template);
  58. $template = str_replace('{LF}', "<?=\"\\n\"?>", $template);
  59. $template = preg_replace("/\{(\\\$[a-zA-Z0-9_\-\>\[\]\'\"\$\.\x7f-\xff]+)\s(or|\?\?)\s([a-zA-Z0-9\']+)\}/s", "{echo \\1 ?? \\3}", $template);
  60. $template = preg_replace("/\{(\\\$[a-zA-Z0-9_\-\>\[\]\'\"\$\.\x7f-\xff]+)\}/s", "<?=\\1?>", $template);
  61. $template = preg_replace_callback('/\{hook\/(\w+?)(\s+(.+?))?\}/i', [$this, 'parse_template_callback_hooktags_13'], $template);
  62. $template = preg_replace_callback("/$var_regexp/s", [$this, 'parse_template_callback_addquote_1'], $template);
  63. $template = preg_replace_callback("/\<\?\=\<\?\=$var_regexp\?\>\?\>/s", [$this, 'parse_template_callback_addquote_1'], $template);
  64. $headeradd = $headerexists ? "hookscriptoutput('$basefile');" : '';
  65. if (!empty($this->subtemplates)) {
  66. $headeradd .= "\n0\n";
  67. foreach ($this->subtemplates as $fname) {
  68. $headeradd .= "|| checktplrefresh('$tplfile', '$fname', " . time() . ", '$templateid', '$cachefile', '$tpldir', '$file')\n";
  69. }
  70. $headeradd .= ';';
  71. }
  72. if (!empty($this->blocks)) {
  73. $headeradd .= "\n";
  74. $headeradd .= "block_get('" . implode(',', $this->blocks) . "');";
  75. }
  76. if (!empty($this->cellFuncs)) {
  77. $headeradd .= "\n";
  78. $headeradd .= implode('', $this->cellFuncs) . ';';
  79. }
  80. if ($headerexists) {
  81. $headeradd .= "if(defined('IN_RESTFUL')) {\$GLOBALS['_L'] = get_defined_vars();exit;}";
  82. }
  83. if ($cachefile) {
  84. $template = "<? if(!defined('IN_DISCUZ')) exit('Access Denied'); {$headeradd}?>\n$template";
  85. }
  86. $template = preg_replace_callback("/[\n\r\t]*\{template\s+([a-z0-9_:\/]+)\}[\n\r\t]*/is", [$this, 'parse_template_callback_stripvtags_template1'], $template);
  87. $template = preg_replace_callback("/[\n\r\t]*\{template\s+(.+?)\}[\n\r\t]*/is", [$this, 'parse_template_callback_stripvtags_template1'], $template);
  88. $template = preg_replace_callback("/[\n\r\t]*\{echo\s+(.+?)\}[\n\r\t]*/is", [$this, 'parse_template_callback_stripvtags_echo1'], $template);
  89. $template = preg_replace_callback("/([\n\r\t]*)\{if\s+(.+?)\}([\n\r\t]*)/is", [$this, 'parse_template_callback_stripvtags_if123'], $template);
  90. $template = preg_replace_callback("/([\n\r\t]*)\{elseif\s+(.+?)\}([\n\r\t]*)/is", [$this, 'parse_template_callback_stripvtags_elseif123'], $template);
  91. $template = preg_replace('/\{else\}/i', '<? } else { ?>', $template);
  92. $template = preg_replace('/\{\/if\}/i', '<? } ?>', $template);
  93. $template = preg_replace_callback("/[\n\r\t]*\{loop\s+(\S+)\s+(\S+)\}[\n\r\t]*/is", [$this, 'parse_template_callback_stripvtags_loop12'], $template);
  94. $template = preg_replace_callback("/[\n\r\t]*\{loop\s+(\S+)\s+(\S+)\s+(\S+)\}[\n\r\t]*/is", [$this, 'parse_template_callback_stripvtags_loop123'], $template);
  95. $template = preg_replace('/\{\/loop\}/i', '<? } ?>', $template);
  96. $template = preg_replace("/\{$const_regexp\}/s", "<?=\\1?>", $template);
  97. if (!empty($this->replacecode)) {
  98. $template = str_replace($this->replacecode['search'], $this->replacecode['replace'], $template);
  99. }
  100. $template = preg_replace("/ \?\>[\n\r]*\<\? /s", ' ', $template);
  101. if ($cachefile && !@$fp = fopen(DISCUZ_DATA . $cachefile, 'c')) {
  102. $this->error('directory_notfound', dirname(DISCUZ_DATA . $cachefile));
  103. }
  104. $template = preg_replace_callback("/\"(http)?[\w\.\/:]+\?[^\"]+?&[^\"]+?\"/", [$this, 'parse_template_callback_transamp_0'], $template);
  105. $template = preg_replace_callback("/\<script[^\>]*?src=\"(.+?)\"(.*?)\>\s*\<\/script\>/is", [$this, 'parse_template_callback_stripscriptamp_12'], $template);
  106. $template = preg_replace_callback("/[\n\r\t]*\{block\s+([a-zA-Z0-9_\[\]']+)\}(.+?)\{\/block\}/is", [$this, 'parse_template_callback_stripblock_12'], $template);
  107. $template = preg_replace('/\<\?(\s{1})/is', "<?php\\1", $template);
  108. $template = preg_replace('/\<\?\=(.+?)\?\>/is', "<?php echo \\1;?>", $template);
  109. if ($this->debug) {
  110. $template = preg_replace_callback("/\<script[\s\w=\/\"]*?\>.+?\<\/script\>/is", [$this, 'parse_template_callback_scriptdebugconvert_0'], $template);
  111. }
  112. if (is_callable($postparse)) {
  113. $template = $postparse($template);
  114. }
  115. if (!($cachefile && $fp && flock($fp, LOCK_EX) && ftruncate($fp, 0) && fwrite($fp, $template) && fflush($fp) && flock($fp, LOCK_UN) && fclose($fp))) {
  116. return $template;
  117. }
  118. }
  119. function parse_template_callback_loadsubtemplate_2($matches) {
  120. return $this->loadsubtemplate($matches[2]);
  121. }
  122. function parse_template_callback_blocktags_1($matches) {
  123. return $this->blocktags($matches[1]);
  124. }
  125. function parse_template_callback_blockdatatags_1($matches) {
  126. return $this->blockdatatags($matches[1]);
  127. }
  128. function parse_template_callback_adtags_1($matches) {
  129. return $this->adtags($matches[1]);
  130. }
  131. function parse_template_callback_adtags_21($matches) {
  132. return $this->adtags($matches[2], $matches[1]);
  133. }
  134. function parse_template_callback_datetags_1($matches) {
  135. return $this->datetags($matches[1]);
  136. }
  137. function parse_template_callback_avatartags_1($matches) {
  138. return $this->avatartags($matches[1]);
  139. }
  140. function parse_template_callback_evaltags_2($matches) {
  141. return $this->evaltags($matches[2]);
  142. }
  143. function parse_template_callback_evaltags_1($matches) {
  144. return $this->evaltags($matches[1]);
  145. }
  146. function parse_template_callback_hooktags_13($matches) {
  147. return $this->hooktags($matches[1], $matches[3] ?? '');
  148. }
  149. function parse_template_callback_addquote_1($matches) {
  150. return $this->addquote('<?=' . $matches[1] . '?>');
  151. }
  152. function parse_template_callback_stripvtags_template1($matches) {
  153. return $this->stripvtags('<? include template(\'' . $matches[1] . '\'); ?>');
  154. }
  155. function parse_template_callback_stripvtags_echo1($matches) {
  156. return $this->stripvtags('<? echo ' . $this->echopolyfill($matches[1]) . '; ?>');
  157. }
  158. function parse_template_callback_stripvtags_if123($matches) {
  159. return $this->stripvtags($matches[1] . '<? if(' . $matches[2] . ') { ?>' . $matches[3]);
  160. }
  161. function parse_template_callback_stripvtags_elseif123($matches) {
  162. return $this->stripvtags($matches[1] . '<? } elseif(' . $matches[2] . ') { ?>' . $matches[3]);
  163. }
  164. function parse_template_callback_stripvtags_loop12($matches) {
  165. return $this->stripvtags($this->looptags($matches[1], $matches[2]));
  166. }
  167. function parse_template_callback_stripvtags_loop123($matches) {
  168. return $this->stripvtags($this->looptags($matches[1], $matches[2], $matches[3]));
  169. }
  170. function parse_template_callback_transamp_0($matches) {
  171. return $this->transamp($matches[0]);
  172. }
  173. function parse_template_callback_stripscriptamp_12($matches) {
  174. return $this->stripscriptamp($matches[1], $matches[2]);
  175. }
  176. function parse_template_callback_stripblock_12($matches) {
  177. return $this->stripblock($matches[1], $matches[2]);
  178. }
  179. function parse_template_callback_scriptdebugconvert_0($matches) {
  180. return $this->scriptdebugconvert($matches[0]);
  181. }
  182. function blocktags($parameter) {
  183. $bid = intval(trim($parameter));
  184. $this->blocks[] = $bid;
  185. $i = count($this->replacecode['search']);
  186. $this->replacecode['search'][$i] = $search = "<!--BLOCK_TAG_$i-->";
  187. $this->replacecode['replace'][$i] = "<?php block_display('$bid');?>";
  188. return $search;
  189. }
  190. function blockdatatags($parameter) {
  191. $bid = intval(trim($parameter));
  192. $this->blocks[] = $bid;
  193. $i = count($this->replacecode['search']);
  194. $this->replacecode['search'][$i] = $search = "<!--BLOCKDATA_TAG_$i-->";
  195. $this->replacecode['replace'][$i] = '';
  196. return $search;
  197. }
  198. function adtags($parameter, $varname = '') {
  199. $parameter = stripslashes($parameter);
  200. $parameter = preg_replace("/(\\\$[a-zA-Z0-9_\-\>\[\]\'\"\$\.\x7f-\xff]+)/s", "{\\1}", $this->addquote($parameter));
  201. $i = count($this->replacecode['search']);
  202. $this->replacecode['search'][$i] = $search = "<!--AD_TAG_$i-->";
  203. $this->replacecode['replace'][$i] = '<?php ' . (!$varname ? 'echo ' : '$' . $varname . '=') . "adshow(\"$parameter\");?>";
  204. return $search;
  205. }
  206. function datetags($parameter) {
  207. $parameter = stripslashes($parameter);
  208. $i = count($this->replacecode['search']);
  209. $this->replacecode['search'][$i] = $search = "<!--DATE_TAG_$i-->";
  210. $this->replacecode['replace'][$i] = "<?php echo dgmdate($parameter);?>";
  211. return $search;
  212. }
  213. function avatartags($parameter) {
  214. $parameter = stripslashes($parameter);
  215. $i = count($this->replacecode['search']);
  216. $this->replacecode['search'][$i] = $search = "<!--AVATAR_TAG_$i-->";
  217. $this->replacecode['replace'][$i] = "<?php echo avatar($parameter);?>";
  218. return $search;
  219. }
  220. function evaltags($php) {
  221. $i = count($this->replacecode['search']);
  222. $this->replacecode['search'][$i] = $search = "<!--EVAL_TAG_$i-->";
  223. $this->replacecode['replace'][$i] = $this->debug ? '<? ' . preg_replace(['/^L\d+[\w\.\/]*\-\-\>/', '/\<\!\-\-L\d+[\w\.\/]*\-\-\>/', '/\<\!\-\-L\d+[\w\.\/]*$/', '/^\s*\<\!\-\-/', '/\-\-\>\s*$/'], '', $php) . '?>' : "<? $php?>";
  224. return $search;
  225. }
  226. function hooktags($hookid, $key = '') {
  227. global $_G;
  228. $i = count($this->replacecode['search']);
  229. $this->replacecode['search'][$i] = $search = "<!--HOOK_TAG_$i-->";
  230. $dev = '';
  231. if (isset($_G['config']['plugindeveloper']) && $_G['config']['plugindeveloper'] == 2) {
  232. $dev = "echo '<hook>[" . ($key ? 'array' : 'string') . " $hookid" . ($key ? '/\'.' . $key . '.\'' : '') . "]</hook>';";
  233. }
  234. $key = $key != '' ? "[$key]" : '';
  235. $this->replacecode['replace'][$i] = "<?php {$dev}if(!empty(\$_G['setting']['pluginhooks']['$hookid']$key)) echo \$_G['setting']['pluginhooks']['$hookid']$key;?>";
  236. return $search;
  237. }
  238. function stripphpcode($type, $code) {
  239. $this->phpcode[$type][] = $code;
  240. return '{phpcode:' . $type . '/' . (count($this->phpcode[$type]) - 1) . '}';
  241. }
  242. function loadsubtemplate($file) {
  243. $tplfile = template($file, 0, '', 1);
  244. $filename = $tplfile;
  245. if ((tplfile::file_exists($filename) && is_readable($filename) && ($content = tplfile::file_get_contents($filename))) ||
  246. (tplfile::file_exists(substr($filename, 0, -4) . '.php') && is_readable(substr($filename, 0, -4) . '.php') && ($content = tplfile::getphptemplate(tplfile::file_get_contents(substr($filename, 0, -4) . '.php'))))) {
  247. $this->subtemplates[] = $tplfile;
  248. return $this->debug ? $this->insertdebugmsg($content, $tplfile) : $content;
  249. } else {
  250. return '<!-- ' . $file . ' -->';
  251. }
  252. }
  253. function looptags($param1, $param2, $param3 = '') {
  254. if (preg_match("/^\<\?\=\\\$.+?\?\>$/s", $param1)) {
  255. $exprtemp = $param1;
  256. $return = '<? if(isset(' . $param1 . ') && is_array(' . $param1 . ')) ';
  257. } else {
  258. $exprtemp = '$l_' . random(8);
  259. $return = '<? ' . $exprtemp . ' = ' . $param1 . ';if(is_array(' . $exprtemp . ')) ';
  260. }
  261. if ($param3) {
  262. $return .= 'foreach(' . $exprtemp . ' as ' . $param2 . ' => ' . $param3 . ') { ?>';
  263. } else {
  264. $return .= 'foreach(' . $exprtemp . ' as ' . $param2 . ') { ?>';
  265. }
  266. return $return;
  267. }
  268. function echopolyfill($str) {
  269. $str = str_replace(' or ', ' ?? ', $str);
  270. if (str_contains($str, ' ?? ') && version_compare(PHP_VERSION, '7.0', '<')) {
  271. $str = preg_replace('/^(.+)\s\?\?\s(.+)$/', "isset(\\1) ? (\\1) : (\\2)", $str);
  272. }
  273. return $str;
  274. }
  275. function transamp($str) {
  276. $str = str_replace('&', '&amp;', $str);
  277. $str = str_replace('&amp;amp;', '&amp;', $str);
  278. return $str;
  279. }
  280. function addquote($var) {
  281. return str_replace("\\\"", "\"", preg_replace_callback("/\[([a-zA-Z0-9_\-\.\x7f-\xff]+)\]/s", [$this, 'addquote_exec'], $var));
  282. }
  283. function addquote_exec($matches) {
  284. return is_numeric($matches[1]) ? '[' . $matches[1] . ']' : "['" . $matches[1] . "']";
  285. }
  286. function stripvtags($expr, $statement = '') {
  287. $expr = str_replace('\\\"', '\"', preg_replace("/\<\?\=(\\\$.+?)\?\>/s", "\\1", $expr));
  288. if ($this->debug) {
  289. $expr = preg_replace('/\<\!\-\-L\d+[\w\.\/]*\-\-\>/', '', $expr);
  290. }
  291. $statement = str_replace('\\\"', '\"', $statement);
  292. return $expr . $statement;
  293. }
  294. function stripscriptamp($s, $extra) {
  295. $s = str_replace('&amp;', '&', $s);
  296. return "<script src=\"$s\" type=\"text/javascript\"$extra></script>";
  297. }
  298. function stripblock($var, $s) {
  299. $var = $this->addquote($var);
  300. $s = preg_replace("/<\?=\\\$(.+?)\?>/", "{\$\\1}", $s);
  301. preg_match_all('/<\?=(.+?)\?>/', $s, $constary);
  302. $constadd = '';
  303. $constary[1] = array_unique($constary[1]);
  304. foreach ($constary[1] as $const) {
  305. $constadd .= '$__' . $const . ' = ' . $const . ';';
  306. }
  307. $s = preg_replace('/<\?=(.+?)\?>/', "{\$__\\1}", $s);
  308. $s = str_replace('?>', "\n\$$var .= <<<EOF\n", $s);
  309. $s = str_replace('<?', "\nEOF;\n", $s);
  310. $s = str_replace("\nphp ", "\n", $s);
  311. return "<?\n$constadd\$$var = <<<EOF\n" . $s . "\nEOF;\n?>";
  312. }
  313. function scriptdebugconvert($str) {
  314. return preg_replace('/\<\!\-\-L(\d+[\w\.\/]*)\-\-\>/', '/**L\1*/', $str);
  315. }
  316. function insertdebugmsg($str, $filename) {
  317. $startmsg = '<!-- BEGIN ' . $filename . ' -->';
  318. $endmsg = '<!-- END ' . $filename . ' -->';
  319. $count = 2;
  320. $debuglevel = $this->debug;
  321. $str = preg_replace_callback('/\n(\t*)/', function ($matches) use (&$count, $filename, $debuglevel) {
  322. if ($debuglevel > 1) {
  323. return "\n" . $matches[1] . '<!--L' . $count++ . $filename . '-->';
  324. } else {
  325. return "\n" . $matches[1] . '<!--L' . $count++ . '-->';
  326. }
  327. }, $str);
  328. return $startmsg . $str . $endmsg;
  329. }
  330. function error($message, $tplname) {
  331. discuz_error::template_error($message, $tplname);
  332. }
  333. }