心理咨询网
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. <?php
  2. /**
  3. * @copyright (C)2016-2099 Hnaoyun Inc.
  4. * @author XingMeng
  5. * @email hnxsh@foxmail.com
  6. * @date 2016年11月6日
  7. * 模板解析引擎
  8. */
  9. namespace core\view;
  10. use core\basic\Config;
  11. class Parser
  12. {
  13. // 模板内容
  14. private static $content;
  15. // 模板路径在嵌套时必须
  16. private static $tplPath;
  17. // 包含文件
  18. private static $tplInc = array();
  19. /**
  20. * 编译公共方法
  21. *
  22. * @param string $tplPath
  23. * 模板主题目录,需要是物理路径
  24. * @param string $tplFile
  25. * 需要解析的模板文件,需要是物理路径
  26. * @return string|mixed
  27. */
  28. public static function compile($tplPath, $tplFile)
  29. {
  30. // 接收模板目录参数
  31. self::$tplPath = $tplPath;
  32. // 读取模板内容
  33. $content = file_get_contents($tplFile) ?: error('模板文件读取错误!' . $tplFile);
  34. // 去除内容Bom信息;
  35. self::$content = ltrim($content, "\xEF\xBB\xBF");
  36. // 解析文件包含,需要优先解析
  37. self::parInclude();
  38. // 添加包含文件记录
  39. self::$content .= "<?php return " . var_export(array_unique(self::$tplInc), 1) . "; ?>";
  40. // =====以下为直接输出方法=========
  41. self::parOutputUrl(); // 输出地址
  42. self::parOutputHomeUrl(); // 输出前端地址
  43. self::parOutputDefine(); // 输出常量
  44. self::parOutputVar(); // 输出变量
  45. self::parOutputObjVal(); // 输出对象
  46. self::parOutputConfig(); // 输出配置参数
  47. self::parOutputSession(); // 输出会话Session
  48. self::parOutputCookie(); // 输出会话Cookie
  49. self::parOutputServer(); // 输出环境变量
  50. self::parOutputPost(); // 输出POST请求值
  51. self::parOutputGet(); // 输出GET请求值
  52. self::parOutputArrVal(); // 输出数组
  53. self::parOutputFun(); // 使用函数
  54. // =========以下为逻辑控制方法==========
  55. self::parIf(); // IF语句
  56. self::parForeachVar(); // Foreach语句
  57. self::parForeachValue(); // Foreach语句嵌套
  58. self::parForeachObj(); // Foreach对象属性
  59. self::parNote(); // 备注
  60. self::parPhp(); // PHP语句
  61. // ============以下为变量解析方法==========
  62. self::parVar(); // 解析变量
  63. self::parObjVar(); // 解析对象
  64. self::parConfigVar(); // 解析配置
  65. self::parSession(); // 解析Session
  66. self::parCookie(); // 解析Cookie
  67. self::parServer(); // 解析环境变量
  68. self::parPost(); // 解析POST请求值
  69. self::parGet(); // 解析GET请求值
  70. self::parArrVar(); // 解析数组
  71. self::parFun(); // 解析函数
  72. // 返回解释的内容
  73. return self::$content;
  74. }
  75. // 解析包含文件,支持多层嵌套
  76. private static function parInclude()
  77. {
  78. $pattern = '/\{include\s+file\s?=\s?([\"\']?)([\w\.\-\/@]+)([\"\']?)\s*\}/';
  79. if (preg_match_all($pattern, self::$content, $matches)) {
  80. $arr = $matches[0]; // 匹配到的所有“包含字符串”:{include file='head.html'}
  81. $brr = $matches[2]; // 包含的文件名:head.html
  82. $count = count($arr);
  83. for ($i = 0; $i < $count; $i ++) {
  84. // 然包含文件支持绝对路径,以/开头
  85. if (strpos($brr[$i], '/') === 0) {
  86. $inc_file = ROOT_PATH . $brr[$i];
  87. } elseif (! ! $pos = strpos($brr[$i], '@')) {
  88. $inc_file = APP_PATH . '/' . substr($brr[$i], 0, $pos) . '/view/' . basename(self::$tplPath) . '/' . substr($brr[$i], $pos + 1);
  89. } else {
  90. if (M == 'home') { // 前台适应模板子目录
  91. $htmldir = Config::get('tpl_html_dir') ? Config::get('tpl_html_dir') . '/' : '';
  92. $inc_file = self::$tplPath . '/' . $htmldir . $brr[$i];
  93. } else {
  94. $inc_file = self::$tplPath . '/' . $brr[$i];
  95. }
  96. }
  97. file_exists($inc_file) ?: error('包含文件不存在!' . $inc_file);
  98. if (! $inc_content = file_get_contents($inc_file)) {
  99. error('包含的模板文件' . $brr[$i] . '读取错误!');
  100. } else {
  101. self::$content = str_replace($arr[$i], $inc_content, self::$content); // 包含内容
  102. self::$tplInc[] = $inc_file;
  103. }
  104. }
  105. // 最大数量不超过50,防止互相包含导致无限循环
  106. if (count(self::$tplInc) < 50) {
  107. self::parInclude();
  108. } else {
  109. error('检测到您模板中包含文件超过50个,请检查是否存在互相包含导致无限循环的情况!');
  110. }
  111. } else {
  112. return false;
  113. }
  114. }
  115. // 解析地址输出 {url./admin/index/index}
  116. private static function parOutputUrl()
  117. {
  118. $pattern = '/\{url\.([^\}]+)\}/';
  119. if (preg_match($pattern, self::$content)) {
  120. self::$content = preg_replace($pattern, "<?php echo \\core\\basic\\Url::get('$1');?>", self::$content);
  121. }
  122. }
  123. // 解析地址输出 {homeurl./home/index/index}
  124. private static function parOutputHomeUrl()
  125. {
  126. $pattern = '/\{homeurl\.([^\}]+)\}/';
  127. if (preg_match($pattern, self::$content)) {
  128. self::$content = preg_replace($pattern, "<?php echo \\core\\basic\\Url::home('$1');?>", self::$content);
  129. }
  130. }
  131. // 解析输出常量 如:{DB_HOST}
  132. private static function parOutputDefine()
  133. {
  134. $pattern = '/\{([A-Z_]+)\}/';
  135. if (preg_match($pattern, self::$content)) {
  136. self::$content = preg_replace($pattern, "<?php echo $1;?>", self::$content);
  137. }
  138. }
  139. // 解析输出普通变量 如:{$name}
  140. private static function parOutputVar()
  141. {
  142. $pattern = '/\{\$([\w]+)\}/';
  143. if (preg_match($pattern, self::$content)) {
  144. self::$content = preg_replace($pattern, "<?php echo \$this->getVar('$1');?>", self::$content);
  145. }
  146. }
  147. // 解析输出对象变量 如:{$user->name}
  148. private static function parOutputObjVal()
  149. {
  150. $pattern = '/\{\$([\w]+)(\->)(\{?)([^}]+)(\}?)\}/';
  151. if (preg_match($pattern, self::$content)) {
  152. self::$content = preg_replace($pattern, "<?php echo @\$this->getVar('$1')$2$3$4$5;?>", self::$content);
  153. }
  154. }
  155. // 解析输出配置 如:{$config.public_app},支持多级
  156. private static function parOutputConfig()
  157. {
  158. $pattern = '/\{\$config\.([\w\.]+)\}/';
  159. if (preg_match_all($pattern, self::$content, $matchs)) {
  160. foreach ($matchs[0] as $key => $value) {
  161. if (strpos($matchs[1][$key], 'database') === false) {
  162. self::$content = str_replace($matchs[0][$key], "<?php print_r(\\core\\basic\\Config::get('" . $matchs[1][$key] . "'));?>", self::$content);
  163. }
  164. }
  165. }
  166. }
  167. // 解析输出Session变量 如:{$session.username},支持多级
  168. private static function parOutputSession()
  169. {
  170. $pattern = '/\{\$session\.([\w\.]+)\}/';
  171. if (preg_match($pattern, self::$content)) {
  172. self::$content = preg_replace($pattern, "<?php echo session('$1');?>", self::$content);
  173. }
  174. }
  175. // 解析输出Cookie变量 如:{$cookie.username}
  176. private static function parOutputCookie()
  177. {
  178. $pattern = '/\{\$cookie\.([\w]+)\}/';
  179. if (preg_match($pattern, self::$content)) {
  180. self::$content = preg_replace($pattern, "<?php echo cookie('$1');?>", self::$content);
  181. }
  182. }
  183. // 解析输出Server变量 如:{$server.PATH_INFO}
  184. private static function parOutputServer()
  185. {
  186. $pattern = '/\{\$server\.([\w\-]+)\}/';
  187. if (preg_match($pattern, self::$content)) {
  188. self::$content = preg_replace($pattern, "<?php echo escape_string(\$_SERVER['$1']);?>", self::$content);
  189. }
  190. }
  191. // 解析输出POST变量 如:{$post.username}
  192. private static function parOutputPost()
  193. {
  194. $pattern = '/\{\$post\.([\w\-]+)\}/';
  195. if (preg_match($pattern, self::$content)) {
  196. self::$content = preg_replace($pattern, "<?php echo post('$1');?>", self::$content);
  197. }
  198. }
  199. // 解析输出GET变量 如:{$get.username}
  200. private static function parOutputGet()
  201. {
  202. $pattern = '/\{\$get\.([\w\-]+)\}/';
  203. if (preg_match($pattern, self::$content)) {
  204. self::$content = preg_replace($pattern, "<?php echo get('$1');?>", self::$content);
  205. }
  206. }
  207. // 解析输出数组变量 如:{$user['name']} 2.0修改为:{$user.name},支持二维数组{$user.name.sex}
  208. private static function parOutputArrVal()
  209. {
  210. $pattern = '/\{\$([\w]+)\.([\w\-]+)(\.([\w\-]+))?\}/';
  211. if (preg_match_all($pattern, self::$content, $matches)) {
  212. foreach ($matches[0] as $key => $value) {
  213. if ($matches[3][$key]) {
  214. self::$content = preg_replace($pattern, "<?php echo @\$this->vars['$1']['$2']['$4'];?>", self::$content);
  215. } else {
  216. self::$content = preg_replace($pattern, "<?php echo @\$this->vars['$1']['$2'];?>", self::$content);
  217. }
  218. }
  219. }
  220. }
  221. // 应用函数 如:{fun=md5('aaa')}
  222. private static function parOutputFun()
  223. {
  224. $pattern = '/\{fun\s?=\s?([^\}]+)\}/';
  225. if (preg_match($pattern, self::$content)) {
  226. self::$content = preg_replace($pattern, "<?php echo $1;?>", self::$content);
  227. }
  228. }
  229. // 解析if语句 例:{if(a==b)}aaaa{else}bbbb{/if}
  230. private static function parIf()
  231. {
  232. $pattern = '/\{if\(([^}]+)\)\s*\}([\s\S]*)\{\/if\}/';
  233. $pattern_if = '/\{if\(([^}]+)\)\s*\}/';
  234. $pattern_end_if = '/\{\/if\}/';
  235. $pattern_else = '/\{else\}/';
  236. // 未配对的if不进行解析
  237. if (preg_match_all($pattern, self::$content, $matches)) {
  238. $count = count($matches[0]);
  239. for ($i = 0; $i < $count; $i ++) {
  240. $content = preg_replace($pattern_if, "<?php if ($1) {?>", $matches[0][$i]);
  241. $content = preg_replace($pattern_end_if, "<?php } ?>", $content);
  242. $content = preg_replace($pattern_else, "<?php } else { ?>", $content);
  243. self::$content = str_replace($matches[0][$i], $content, self::$content);
  244. }
  245. }
  246. }
  247. // 解析循环语句 {foreach $var(key,value,num)}...[num][value->name]或[value]...{/foreach}
  248. private static function parForeachVar()
  249. {
  250. $pattern_foreach = '/\{foreach\s+\$([\w]+)\(([\w]+),([\w]+)(,([\w]+))?\)\}/';
  251. $pattern_end_foreach = '/\{\/foreach\}/';
  252. if (preg_match_all($pattern_foreach, self::$content, $matches)) {
  253. $count = count($matches[0]);
  254. for ($i = 0; $i < $count; $i ++) {
  255. if (! $matches[5][$i]) {
  256. $matches[5][$i] = 'num';
  257. }
  258. // 解析首标签
  259. self::$content = str_replace($matches[0][$i], "<?php \$" . $matches[5][$i] . " = 0;foreach (\$this->getVar('" . $matches[1][$i] . "') as \$" . $matches[2][$i] . " => \$" . $matches[3][$i] . ") { \$" . $matches[5][$i] . "++;?>", self::$content);
  260. // 解析序号
  261. $pattern_num = '/\[(' . $matches[5][$i] . ')\]/';
  262. if (preg_match($pattern_num, self::$content)) {
  263. if (defined('PAGE')) {
  264. self::$content = preg_replace($pattern_num, "<?php echo @(PAGE-1)*PAGESIZE+\$$1; ?>", self::$content);
  265. } else {
  266. self::$content = preg_replace($pattern_num, "<?php echo \$$1; ?>", self::$content);
  267. }
  268. }
  269. // 解析key
  270. $pattern_key = '/\[(' . $matches[2][$i] . ')\]/';
  271. if (preg_match($pattern_key, self::$content)) {
  272. self::$content = preg_replace($pattern_key, "<?php echo \$$1; ?>", self::$content);
  273. }
  274. // 解析内部变量
  275. $pattern_var = '/\[(' . $matches[3][$i] . ')(\[[\'\"][\w]+[\'\"]\])?(\-\>[\w$]+)?\]/';
  276. self::$content = preg_replace($pattern_var, "<?php echo \$$1$2$3; ?>", self::$content);
  277. }
  278. // 解析闭合标签
  279. self::$content = str_replace('{/foreach}', "<?php } ?>", self::$content);
  280. }
  281. }
  282. // 解析循环语句嵌套 {foreach $value->name(key,value,num)}...[num][value->name]或[value]...{/foreach}
  283. private static function parForeachValue()
  284. {
  285. $pattern_foreach = '/\{foreach\s+\$([\w][\w\->]+)\(([\w]+),([\w]+)(,([\w]+))?\)\}/';
  286. $pattern_end_foreach = '/\{\/foreach\}/';
  287. if (preg_match_all($pattern_foreach, self::$content, $matches)) {
  288. $count = count($matches[0]);
  289. for ($i = 0; $i < $count; $i ++) {
  290. if (! $matches[5][$i]) {
  291. $matches[5][$i] = 'num';
  292. }
  293. // 解析首标签
  294. self::$content = str_replace($matches[0][$i], "<?php \$" . $matches[5][$i] . " = 0;foreach (\$" . $matches[1][$i] . " as \$" . $matches[2][$i] . " => \$" . $matches[3][$i] . ") { \$" . $matches[5][$i] . "++;?>", self::$content);
  295. // 解析序号
  296. $pattern_num = '/\[(' . $matches[5][$i] . ')\]/';
  297. if (preg_match($pattern_num, self::$content)) {
  298. if (defined('PAGE')) {
  299. self::$content = preg_replace($pattern_num, "<?php echo (PAGE-1)*PAGESIZE+\$$1; ?>", self::$content);
  300. } else {
  301. self::$content = preg_replace($pattern_num, "<?php echo \$$1; ?>", self::$content);
  302. }
  303. }
  304. // 解析key
  305. $pattern_key = '/\[(' . $matches[2][$i] . ')\]/';
  306. if (preg_match($pattern_key, self::$content)) {
  307. self::$content = preg_replace($pattern_key, "<?php echo \$$1; ?>", self::$content);
  308. }
  309. // 解析内部变量
  310. $pattern_var = '/\[(' . $matches[3][$i] . ')(\[[\'\"][\w]+[\'\"]\])?(\-\>[\w$]+)?\]/';
  311. self::$content = preg_replace($pattern_var, "<?php echo \$$1$2$3; ?>", self::$content);
  312. }
  313. // 解析闭合标签
  314. self::$content = str_replace('{/foreach}', "<?php }?>", self::$content);
  315. }
  316. }
  317. // 解析循环语句 {foreach [$var->name](key,value,num)}...{/foreach}
  318. private static function parForeachObj()
  319. {
  320. $pattern_foreach = '/\{foreach\s+\[\$([\w]+)\-\>([\w]+)\]\(([\w]+),([\w]+)(,([\w]+))?\)\}/';
  321. $pattern_end_foreach = '/\{\/foreach\}/';
  322. if (preg_match_all($pattern_foreach, self::$content, $matches)) {
  323. $count = count($matches[0]);
  324. for ($i = 0; $i < $count; $i ++) {
  325. if (! $matches[6][$i]) {
  326. $matches[6][$i] = 'num';
  327. }
  328. // 解析首标签
  329. self::$content = str_replace($matches[0][$i], "<?php \$" . $matches[6][$i] . " = 0;foreach (\$this->getVar('" . $matches[1][$i] . "')->" . $matches[2][$i] . " as \$" . $matches[3][$i] . " => \$" . $matches[4][$i] . ") { \$" . $matches[6][$i] . "++;?>", self::$content);
  330. // 解析序号
  331. $pattern_num = '/\[(' . $matches[6][$i] . ')\]/';
  332. if (preg_match($pattern_num, self::$content)) {
  333. if (defined('PAGE')) {
  334. self::$content = preg_replace($pattern_num, "<?php echo @(PAGE-1)*PAGESIZE+\$$1; ?>", self::$content);
  335. } else {
  336. self::$content = preg_replace($pattern_num, "<?php echo \$$1; ?>", self::$content);
  337. }
  338. }
  339. // 解析key
  340. $pattern_key = '/\[(' . $matches[3][$i] . ')\]/';
  341. if (preg_match($pattern_key, self::$content)) {
  342. self::$content = preg_replace($pattern_key, "<?php echo \$$1; ?>", self::$content);
  343. }
  344. // 解析内部变量
  345. $pattern_var = '/\[(' . $matches[4][$i] . ')(\[[\'\"][\w]+[\'\"]\])?(\-\>[\w$]+)?\]/';
  346. self::$content = preg_replace($pattern_var, "<?php echo \$$1$2$3; ?>", self::$content);
  347. }
  348. // 解析闭合标签
  349. self::$content = str_replace('{/foreach}', "<?php } ?>", self::$content);
  350. }
  351. }
  352. // PHP代码注释{#}...{#}
  353. private static function parNote()
  354. {
  355. $pattern = '/\{#\}(\s\S]*?)\{#\}/';
  356. if (preg_match($pattern, self::$content)) {
  357. self::$content = preg_replace($pattern, "<?php
  358. /* $1 */
  359. ?>", self::$content);
  360. }
  361. }
  362. // 原生PHP代码{php}...{/php}
  363. private static function parPhp()
  364. {
  365. $pattern = '/\{php\}([\s\S]*?)\{\/php\}/';
  366. if (preg_match($pattern, self::$content)) {
  367. self::$content = preg_replace($pattern, "<?php $1?>", self::$content);
  368. }
  369. }
  370. // 解析变量[$varname]
  371. private static function parVar()
  372. {
  373. $pattern = '/\[\$([\w]+)\]/';
  374. if (preg_match($pattern, self::$content)) {
  375. self::$content = preg_replace($pattern, "\$this->getVar('$1')", self::$content);
  376. }
  377. }
  378. // 解析对象变量 [$user->name]
  379. private static function parObjVar()
  380. {
  381. $pattern = '/\[\$([\w]+)\-\>([\w\$]+)\]/';
  382. if (preg_match($pattern, self::$content)) {
  383. self::$content = preg_replace($pattern, "\$this->getVar('$1')->$2", self::$content);
  384. }
  385. }
  386. // 解析配置变量[$config.name],支持多级
  387. private static function parConfigVar()
  388. {
  389. $pattern = '/\[\$config\.([\w\.]+)\]/';
  390. if (preg_match($pattern, self::$content)) {
  391. self::$content = preg_replace($pattern, "\\core\\basic\\Config::get('$1')", self::$content);
  392. }
  393. if (preg_match_all($pattern, self::$content, $matchs)) {
  394. foreach ($matchs[0] as $key => $value) {
  395. if (strpos($matchs[1][$key], 'database') === false) {
  396. self::$content = str_replace($matchs[0][$key], "\\core\\basic\\Config::get('" . $matchs[1][$key] . "')", self::$content);
  397. }
  398. }
  399. }
  400. }
  401. // 解析Session [$session.name],支持多级
  402. private static function parSession()
  403. {
  404. $pattern = '/\[\$session\.([\w\.]+)\]/';
  405. if (preg_match($pattern, self::$content)) {
  406. self::$content = preg_replace($pattern, "session('$1')", self::$content);
  407. }
  408. }
  409. // 解析Cookie [$cookie.name]
  410. private static function parCookie()
  411. {
  412. $pattern = '/\[\$cookie\.([\w]+)\]/';
  413. if (preg_match($pattern, self::$content)) {
  414. self::$content = preg_replace($pattern, "cookie('$1')", self::$content);
  415. }
  416. }
  417. // 解析Server [$server.name]
  418. private static function parServer()
  419. {
  420. $pattern = '/\[\$server\.([\w]+)\]/';
  421. if (preg_match($pattern, self::$content)) {
  422. self::$content = preg_replace($pattern, "escape_string(\$_SERVER['$1'])", self::$content);
  423. }
  424. }
  425. // 解析POST [$post.id]
  426. private static function parPost()
  427. {
  428. $pattern = '/\[\$post\.([\w]+)\]/';
  429. if (preg_match($pattern, self::$content)) {
  430. self::$content = preg_replace($pattern, "post('$1')", self::$content);
  431. }
  432. }
  433. // 解析GET[$get.id]
  434. private static function parGet()
  435. {
  436. $pattern = '/\[\$get\.([\w]+)\]/';
  437. if (preg_match($pattern, self::$content)) {
  438. self::$content = preg_replace($pattern, "get('$1')", self::$content);
  439. }
  440. }
  441. // 解析数组变量 [$user['name']] 2.0修改为[$user.name],支持二维数组[$user.name.sex]
  442. private static function parArrVar()
  443. {
  444. $pattern = '/\[\$([\w]+)\.([\w\-]+)(\.([\w\-]+))?\]/';
  445. if (preg_match_all($pattern, self::$content, $matches)) {
  446. foreach ($matches[0] as $key => $value) {
  447. if ($matches[3][$key]) {
  448. self::$content = preg_replace($pattern, "\$this->vars['$1']['$2']['$4']", self::$content);
  449. } else {
  450. self::$content = preg_replace($pattern, "\$this->vars['$1']['$2']", self::$content);
  451. }
  452. }
  453. }
  454. }
  455. // 内部应用函数 如:[fun=md5('aaa')]
  456. private static function parFun()
  457. {
  458. $pattern = '/\[fun=([^\]]+)\]/';
  459. if (preg_match($pattern, self::$content)) {
  460. self::$content = preg_replace($pattern, "$1", self::$content);
  461. }
  462. }
  463. }