Think.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: liu21st <liu21st@gmail.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types = 1);
  12. namespace think\view\driver;
  13. use think\App;
  14. use think\helper\Str;
  15. use think\Template;
  16. use think\template\exception\TemplateNotFoundException;
  17. class Think
  18. {
  19. // 模板引擎实例
  20. private $template;
  21. // 模板引擎参数
  22. protected $config = [
  23. // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法
  24. 'auto_rule' => 1,
  25. // 视图目录名
  26. 'view_dir_name' => 'view',
  27. // 模板起始路径
  28. 'view_path' => '',
  29. // 模板文件后缀
  30. 'view_suffix' => 'html',
  31. // 模板文件名分隔符
  32. 'view_depr' => DIRECTORY_SEPARATOR,
  33. // 是否开启模板编译缓存,设为false则每次都会重新编译
  34. 'tpl_cache' => true,
  35. ];
  36. public function __construct(private App $app, array $config = [])
  37. {
  38. $this->config = array_merge($this->config, (array) $config);
  39. if (empty($this->config['cache_path'])) {
  40. $this->config['cache_path'] = $app->getRuntimePath() . 'temp' . DIRECTORY_SEPARATOR;
  41. }
  42. $this->template = new Template($this->config);
  43. $this->template->setCache($app->cache);
  44. $this->template->extend('$Think', function (array $vars) {
  45. $type = strtoupper(trim(array_shift($vars)));
  46. $param = implode('.', $vars);
  47. return match ($type) {
  48. 'CONST' => strtoupper($param),
  49. 'CONFIG' => 'config(\'' . $param . '\')',
  50. 'LANG' => 'lang(\'' . $param . '\')',
  51. 'NOW' => "date('Y-m-d g:i a',time())",
  52. 'LDELIM' => '\'' . ltrim($this->getConfig('tpl_begin'), '\\') . '\'',
  53. 'RDELIM' => '\'' . ltrim($this->getConfig('tpl_end'), '\\') . '\'',
  54. default => defined($type) ? $type : '\'\'',
  55. };
  56. });
  57. $this->template->extend('$Request', function (array $vars) {
  58. // 获取Request请求对象参数
  59. $method = array_shift($vars);
  60. if (!empty($vars)) {
  61. $params = implode('.', $vars);
  62. if ('true' != $params) {
  63. $params = '\'' . $params . '\'';
  64. }
  65. } else {
  66. $params = '';
  67. }
  68. return 'app(\'request\')->' . $method . '(' . $params . ')';
  69. });
  70. }
  71. /**
  72. * 检测是否存在模板文件
  73. * @access public
  74. * @param string $template 模板文件或者模板规则
  75. * @return bool
  76. */
  77. public function exists(string $template): bool
  78. {
  79. if ('' == pathinfo($template, PATHINFO_EXTENSION)) {
  80. // 获取模板文件名
  81. $template = $this->parseTemplate($template);
  82. }
  83. return is_file($template);
  84. }
  85. /**
  86. * 渲染模板文件
  87. * @access public
  88. * @param string $template 模板文件
  89. * @param array $data 模板变量
  90. * @return void
  91. */
  92. public function fetch(string $template, array $data = []): void
  93. {
  94. if (empty($this->config['view_path'])) {
  95. $view = $this->config['view_dir_name'];
  96. if (is_dir($this->app->getAppPath() . $view)) {
  97. $path = $this->app->getAppPath() . $view . DIRECTORY_SEPARATOR;
  98. } else {
  99. $appName = $this->app->http->getName();
  100. $path = $this->app->getRootPath() . $view . DIRECTORY_SEPARATOR . ($appName ? $appName . DIRECTORY_SEPARATOR : '');
  101. }
  102. $this->config['view_path'] = $path;
  103. $this->template->view_path = $path;
  104. }
  105. if ('' == pathinfo($template, PATHINFO_EXTENSION)) {
  106. // 获取模板文件名
  107. $template = $this->parseTemplate($template);
  108. }
  109. // 模板不存在 抛出异常
  110. if (!is_file($template)) {
  111. throw new TemplateNotFoundException('template not exists:' . $template, $template);
  112. }
  113. $this->template->fetch($template, $data);
  114. }
  115. /**
  116. * 渲染模板内容
  117. * @access public
  118. * @param string $template 模板内容
  119. * @param array $data 模板变量
  120. * @return void
  121. */
  122. public function display(string $template, array $data = []): void
  123. {
  124. $this->template->display($template, $data);
  125. }
  126. /**
  127. * 自动定位模板文件
  128. * @access private
  129. * @param string $template 模板文件规则
  130. * @return string
  131. */
  132. private function parseTemplate(string $template): string
  133. {
  134. // 分析模板文件规则
  135. $request = $this->app['request'];
  136. // 获取视图根目录
  137. if (strpos($template, '@')) {
  138. // 跨模块调用
  139. list($app, $template) = explode('@', $template);
  140. }
  141. if (isset($app)) {
  142. $view = $this->config['view_dir_name'];
  143. $viewPath = $this->app->getBasePath() . $app . DIRECTORY_SEPARATOR . $view . DIRECTORY_SEPARATOR;
  144. if (is_dir($viewPath)) {
  145. $path = $viewPath;
  146. } else {
  147. $path = $this->app->getRootPath() . $view . DIRECTORY_SEPARATOR . $app . DIRECTORY_SEPARATOR;
  148. }
  149. $this->template->view_path = $path;
  150. } else {
  151. $path = $this->config['view_path'];
  152. }
  153. $depr = $this->config['view_depr'];
  154. if (0 !== strpos($template, '/')) {
  155. $template = str_replace(['/', ':'], $depr, $template);
  156. $controller = $request->controller();
  157. if (strpos($controller, '.')) {
  158. $pos = strrpos($controller, '.');
  159. $controller = substr($controller, 0, $pos) . '.' . Str::snake(substr($controller, $pos + 1));
  160. } else {
  161. $controller = Str::snake($controller);
  162. }
  163. if ($controller) {
  164. if ('' == $template) {
  165. // 如果模板文件名为空 按照默认模板渲染规则定位
  166. if (2 == $this->config['auto_rule']) {
  167. $template = $request->action(true);
  168. } elseif (3 == $this->config['auto_rule']) {
  169. $template = $request->action();
  170. } else {
  171. $template = Str::snake($request->action());
  172. }
  173. $template = str_replace('.', DIRECTORY_SEPARATOR, $controller) . $depr . $template;
  174. } elseif (false === strpos($template, $depr)) {
  175. $template = str_replace('.', DIRECTORY_SEPARATOR, $controller) . $depr . $template;
  176. }
  177. }
  178. } else {
  179. $template = str_replace(['/', ':'], $depr, substr($template, 1));
  180. }
  181. return $path . ltrim($template, '/') . '.' . ltrim($this->config['view_suffix'], '.');
  182. }
  183. /**
  184. * 配置模板引擎
  185. * @access private
  186. * @param array $config 参数
  187. * @return void
  188. */
  189. public function config(array $config): void
  190. {
  191. $this->template->config($config);
  192. $this->config = array_merge($this->config, $config);
  193. }
  194. /**
  195. * 获取模板引擎配置
  196. * @access public
  197. * @param string $name 参数名
  198. * @return void
  199. */
  200. public function getConfig(string $name)
  201. {
  202. return $this->template->getConfig($name);
  203. }
  204. public function __call($method, $params)
  205. {
  206. return call_user_func_array([$this->template, $method], $params);
  207. }
  208. }