// +---------------------------------------------------------------------- declare(strict_types=1); namespace think\view\driver; use app\common\tools\PathTools; use think\App; use think\facade\Env; use think\helper\Arr; use think\helper\Str; use think\Template; use think\template\exception\TemplateNotFoundException; class Think { // 模板引擎实例 private $template; // 模板引擎参数 protected $config = [ // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法 'auto_rule' => 1, // 视图目录名 'view_dir_name' => 'view', // 模板文件后缀 'view_suffix' => 'html', // 模板文件名分隔符 'view_depr' => DIRECTORY_SEPARATOR, // 是否开启模板编译缓存,设为false则每次都会重新编译 'tpl_cache' => true, ]; public $data = []; public function __construct(private App $app, array $config = []) { $this->config = array_merge($this->config, (array) $config); if (empty($this->config['cache_path'])) { $this->config['cache_path'] = $app->getRuntimePath() . 'temp' . DIRECTORY_SEPARATOR; } $this->template = new Template($this->config); $this->template->setCache($app->cache); $this->template->extend('$Think', function (array $vars) { $type = strtoupper(trim(array_shift($vars))); $param = implode('.', $vars); return match ($type) { 'CONST' => strtoupper($param), 'CONFIG' => 'config(\'' . $param . '\')', 'LANG' => 'lang(\'' . $param . '\')', 'NOW' => "date('Y-m-d g:i a',time())", 'LDELIM' => '\'' . ltrim($this->getConfig('tpl_begin'), '\\') . '\'', 'RDELIM' => '\'' . ltrim($this->getConfig('tpl_end'), '\\') . '\'', default => defined($type) ? $type : '\'\'', }; }); $this->template->extend('$Request', function (array $vars) { // 获取Request请求对象参数 $method = array_shift($vars); if (!empty($vars)) { $params = implode('.', $vars); if ('true' != $params) { $params = '\'' . $params . '\''; } } else { $params = ''; } return 'app(\'request\')->' . $method . '(' . $params . ')'; }); $this->template->extend('$View', function (array $vars) { $params = implode('.', $vars); return "'" . Arr::get($this->data, $params, '') . "'"; }); $this->template->extend('$Controller', function (array $vars) { // 获取Request请求对象参数 $method = array_shift($vars); if (!empty($vars)) { $params = implode('.', $vars); if ('true' != $params) { $params = '\'' . $params . '\''; } } else { $params = ''; } return '$Controller->' . $method . '(' . $params . ')'; }); } /** * 检测是否存在模板文件. * @param string $template 模板文件或者模板规则 * @return bool */ public function exists(string $template): bool { if ('' == pathinfo($template, PATHINFO_EXTENSION)) { // 获取模板文件名 $template = $this->parseTemplate($template); } return is_file($template); } /** * 渲染模板文件. * @param string $template 模板文件 * @param array $data 模板变量 * @return string */ public function fetch(string $template, array $data = []): string { if ('' == pathinfo($template, PATHINFO_EXTENSION)) { // 获取模板文件名 $template = $this->parseTemplate($template); } // 模板不存在 抛出异常 if (!is_file($template)) { throw new TemplateNotFoundException('template not exists:' . $template, $template); } $this->data = array_merge($this->data, $data); return $this->template->fetch($template, $data); } /** * 渲染模板内容. * @param string $template 模板内容 * @param array $data 模板变量 * @return void */ public function display(string $template, array $data = []): void { $this->template->display($template, $data); } /** * 自动定位模板文件. * @param string $template 模板文件规则 * @return string */ private function parseTemplate(string $template): string { // 分析模板文件规则 $request = $this->app['request']; // 获取视图根目录 if (strpos($template, '@') !== false) { // 跨模块调用 list($app, $template) = explode('@', $template); } else { $app = $this->app->http->getName(); } $view = $this->config['view_dir_name']; if (empty($app)) { if (str_starts_with($template, '/')) { $app_path = $this->app->getRootPath(); $base_app_path = $this->app->getRootPath(); $view_app_path = $this->app->getRootPath(); } else { // $app不应该为空,如果为空,说明是@开头的用法,此时定位当前应用控制器下的用法 $app = $this->app->http->getName(); $app_path = $this->app->getAppPath() . $view . DIRECTORY_SEPARATOR; $base_app_path = $this->app->getRootPath() . 'extend' . DIRECTORY_SEPARATOR . 'base' . DIRECTORY_SEPARATOR . $app . DIRECTORY_SEPARATOR . $view . DIRECTORY_SEPARATOR; $view_app_path = $this->app->getRootPath() . $view . DIRECTORY_SEPARATOR . ($app ? $app . DIRECTORY_SEPARATOR : ''); } } else { $app_path = $this->app->getAppPath() . $view . DIRECTORY_SEPARATOR; $base_app_path = $this->app->getRootPath() . 'extend' . DIRECTORY_SEPARATOR . 'base' . DIRECTORY_SEPARATOR . $app . DIRECTORY_SEPARATOR . $view . DIRECTORY_SEPARATOR; $view_app_path = $this->app->getRootPath() . $view . DIRECTORY_SEPARATOR . ($app ? $app . DIRECTORY_SEPARATOR : ''); } $this->template->view_root_path = $this->app->getRootPath(); $this->template->app_path = $app_path; $this->template->base_app_path = $base_app_path; $this->template->view_app_path = $view_app_path; $depr = $this->config['view_depr']; if (0 !== strpos($template, '/')) { $template = str_replace(['/', ':'], $depr, $template); $controller = $request->controller(); if (strpos($controller, '.')) { $pos = strrpos($controller, '.'); $controller = substr($controller, 0, $pos) . '.' . Str::snake(substr($controller, $pos + 1)); } else { $controller = Str::snake($controller); } if ($controller) { if ('' == $template) { // 如果模板文件名为空 按照默认模板渲染规则定位 if (2 == $this->config['auto_rule']) { $template = $request->action(true); } elseif (3 == $this->config['auto_rule']) { $template = $request->action(); } else { $template = Str::snake($request->action()); } $template = str_replace('.', DIRECTORY_SEPARATOR, $controller) . $depr . $template; } elseif (false === strpos($template, $depr)) { $template = str_replace('.', DIRECTORY_SEPARATOR, $controller) . $depr . $template; } } } else { $template = str_replace(['/', ':'], $depr, substr($template, 1)); } $view_file_path = ltrim($template, '/') . '.' . ltrim($this->config['view_suffix'], '.'); $file_path = ''; $default_file_path = $app_path . $view_file_path; if (is_file($default_file_path)) { // 优先app下的view $file_path = $default_file_path; } elseif (is_file($base_app_path . $view_file_path)) { // 查找extend下的view $file_path = $base_app_path . $view_file_path; } elseif (is_file($view_app_path . $view_file_path)) { // 查找根目录下的view $file_path = $view_app_path . $view_file_path; } else { $file_path = $default_file_path; if (Env::get('adminsystem.make_view_while_missing', false)) { PathTools::intiDir($file_path); file_put_contents($file_path, ''); } } $this->template->base_view_path = dirname($file_path) . DIRECTORY_SEPARATOR; return $file_path; } /** * 配置模板引擎. * @param array $config 参数 * @return void */ public function config(array $config): void { $this->template->config($config); $this->config = array_merge($this->config, $config); } /** * 获取模板引擎配置. * @param string $name 参数名 * @return void|string */ public function getConfig(string $name) { return $this->template->getConfig($name); } public function __call($method, $params) { return call_user_func_array([$this->template, $method], $params); } }