tp6源码解析-第二天,ThinkPHP6编译模板流程详解,ThinkPHP6模板源码详解

TP6源码解析,ThinkPHP6模板编译流程详解

前言:刚开始写博客。如果觉得本篇文章对您有所帮助。点个赞再走也不迟

模板编译流程,大概是:

  1. 先获取到View类实例(依赖注入也好,通过助手函数也好)
  2. 使用View编译方法fetch或display。都会通过engine方法获取到当前的模板驱动
  3. 把模板以及要编译的数据传入到驱动中对应的display方法或fetch方法
  4. display和fetch差不多(个人感觉),这俩的大致流程都是
  5. 判断缓存文件失效没,如果失效重新编译放入缓存目录。如果没失效则直接读取输出或返回

1、app\controller\Index->Index() 控制器方法

 

public function index(View $view)
{
    //定义一个data变量,是个关联数组
    $data = [
        'data'=>'我是变量data',
        'msg'=>'我是变量msg'
    ];
    //直接调用View实例中的fetch方法进行渲染。并把data传入进去
    return    $view->fetch('index',$data);
    //第二种方式
   $strings = "{$data}-{$msg}";
   return   $view->assign($data)->display($strings,$data);
}

  

上图代码所示,tp的渲染模板。默认有两种方式,一种是调用fetch直接进行渲染。第二种就是先调用assign方法传入要渲染的数据。然后再调用display方法进行渲染。(还有助手函数view().这里就不过多描述了)。

2、接下来就讲讲tp是如何将数据渲染到模板上。并且输出
①第一种方式
首先会调用\Think\View视图类中的fetch方法

public function fetch(string $template = '', array $vars = []): string
{
    //调用类中的getContent方法,并将模板文件、渲染的数据
    //封装成匿名函数传入到getContent方法中
    return $this->getContent(function () use ($vars, $template) {
        $this->engine()->fetch($template, array_merge($this->data, $vars));
    });
}

  

接下来看下getContent方法

//这个方法,主要就是把传入过来的匿名函数给运行一下
//通过ob_系列函数捕捉到缓冲区的输出
//然后将捕捉到的缓冲区内容返回
protected function getContent($callback): string
{
    //开启缓存
    ob_start();
    //取消缓存输出到页面上
    ob_implicit_flush(0);
    //开始渲染
    try {
        //执行闭包
        $callback();
    } catch (\Exception $e) {
        //如果闭包函数执行失败则清空缓存
        ob_end_clean();
        //然后弹出异常
        throw $e;
    }
    //获取并清空缓存
    $content = ob_get_clean();
    //如果filter有闭包。一般为替换当前页面的什么东西。执行一下
    if ($this->filter) {
        $content = call_user_func_array($this->filter, [$content]);
    }
    //最后直接返回
    return $content;
}

  

传入的闭包内容

//大概就是获取视图驱动,然后传入模板和数据,渲染模板
 $this->engine()->fetch($template, array_merge($this->data, $vars));

  

追踪到engine方法

//这个就不多解释什么了,主要就是获取视图驱动的实例
public function engine(string $type = null)
{
    return $this->driver($type);
}

  

再追踪到tp默认视图驱动中的fetch方法

public function fetch(string $template, array $vars = []): void
    //判断是否有数据如果有则合并this->data
    if ($vars) {
        $this->data = array_merge($this->data, $vars);
    }
    //判断有没有缓存,如果有。则直接输出缓存
    if (!empty($this->config['cache_id']) && $this->config['display_cache'] && $this->cache) {
        // 读取渲染缓存
        if ($this->cache->has($this->config['cache_id'])) {
            echo $this->cache->get($this->config['cache_id']);
            return;
        }
    }
    //解析传入过来的模板名
    $template = $this->parseTemplateFile($template);
    if ($template) {
        //这里生成一下缓存路径+模板
        $cacheFile = $this->config['cache_path'] . $this->config['cache_prefix'] . md5($this->config['layout_on'] . $this->config['layout_name'] . $template) . '.' . ltrim($this->config['cache_suffix'], '.');
        //判断缓存文件是否还有效,或者存在
        if (!$this->checkCache($cacheFile)) {
            // 缓存无效 重新模板编译
            //如果不存在,读取模板。并调用compiler重新编译
            $content = file_get_contents($template);
            $this->compiler($content, $cacheFile);
        }
        //开启页面缓存
        ob_start();
        //禁止直接输出
        ob_implicit_flush(0);
        // 读取编译存储
        $this->storage->read($cacheFile, $this->data);
        // 获取并清空缓存
        $content = ob_get_clean();
        if (!empty($this->config['cache_id']) && $this->config['display_cache'] && $this->cache) {
            // 缓存页面输出
            $this->cache->set($this->config['cache_id'], $content, $this->config['cache_time']);
        }
        //输出编译后的内容
        echo $content;
    }
}

  

这里主要是编译模板的方法 compiler。主要就是把模板内的文件都替换掉

private function compiler(string &$content, string $cacheFile): void
{
    // 判断是否启用布局
    if ($this->config['layout_on']) {
        if (false !== strpos($content, '{__NOLAYOUT__}')) {
            // 可以单独定义不使用布局
            $content = str_replace('{__NOLAYOUT__}', '', $content);
        } else {
            // 读取布局模板
            $layoutFile = $this->parseTemplateFile($this->config['layout_name']);
            if ($layoutFile) {
                // 替换布局的主体内容
                $content = str_replace($this->config['layout_item'], $content, file_get_contents($layoutFile));
            }
        }
    } else {
        $content = str_replace('{__NOLAYOUT__}', '', $content);
    }
    //这里是替换模板内的tp语法例如 {$data} = echo $data
    //{if (1==1))} ==  <?php if (1==1): ?>
    //{/endif}   == <?php endif;?>
    $this->parse($content);
    if ($this->config['strip_space']) {
        /* 去除html空格与换行 */
        $find    = ['~>\s+<~', '~>(\s+\n|\r)~'];
        $replace = ['><', '>'];
        $content = preg_replace($find, $replace, $content);
    }
    // 优化生成的php代码
    $content = preg_replace('/\?>\s*<\?php\s(?!echo\b|\bend)/s', '', $content);
    // 模板过滤输出
    $replace = $this->config['tpl_replace_string'];
    $content = str_replace(array_keys($replace), array_values($replace), $content);
    // 添加安全代码及模板引用记录
    $content = '<?php /*' . serialize($this->includeFile) . '*/ ?>' . "\n" . $content;
    //写入缓存
    $this->storage->write($cacheFile, $content);
    $this->includeFile = [];
}

  

接着回到fetch方法中

 // 读取编译存储
 $this->storage->read($cacheFile, $this->data);

  

这里是获取到\Think\template\driver\File类实例并且将缓存文件和数据传入到read方法中

public function read(string $cacheFile, array $vars = []): void
{
    $this->cacheFile = $cacheFile;
    if (!empty($vars) && is_array($vars)) {
        //通过extract方法,将数据写到符号表内
        extract($vars, EXTR_OVERWRITE);
    }
    //载入模版缓存文件
    include $this->cacheFile;
}

  

到这里,第一种方式流程就算是完了
第二种、display方法去渲染

//这个和第一种流程第一步一样,不多解释
public function display(string $content, array $vars = []): string
{
    return $this->getContent(function () use ($vars, $content) {
        //调用template中的display方法,并将content和数据传入进去
        $this->engine()->display($content, array_merge($this->data, $vars));
    });
}

  

追踪到Template类中的display方法

public function display(string $content, array $vars = []): void
{
    //判断有没有要编译的数据/如果有就和this->data叔祖1合并
    if ($vars) {
        $this->data = array_merge($this->data, $vars);
    }
    //获取到编译后的缓存模板
    $cacheFile = $this->config['cache_path'] . $this->config['cache_prefix'] . md5($content) . '.' . ltrim($this->config['cache_suffix'], '.');
    //调用checkCahce把缓存模板传入进去,查看是否过期
    //或不存在
    if (!$this->checkCache($cacheFile)) {
        //如果模板无效或不存在,就调用
        //compiler方法重写缓存
        $this->compiler($content, $cacheFile);
    }
    // 读取编译存储
    $this->storage->read($cacheFile, $this->data);
}

  

到这里整个流程就结束了,有兴趣的可以看下我另外一篇文章

 

posted @ 2020-04-14 17:12  Death-Satan  阅读(3213)  评论(1编辑  收藏  举报