tp6源码解析-第二天,ThinkPHP6编译模板流程详解,ThinkPHP6模板源码详解
TP6源码解析,ThinkPHP6模板编译流程详解
前言:刚开始写博客。如果觉得本篇文章对您有所帮助。点个赞再走也不迟
模板编译流程,大概是:
- 先获取到View类实例(依赖注入也好,通过助手函数也好)
- 使用View编译方法fetch或display。都会通过engine方法获取到当前的模板驱动
- 把模板以及要编译的数据传入到驱动中对应的display方法或fetch方法
- display和fetch差不多(个人感觉),这俩的大致流程都是
- 判断缓存文件失效没,如果失效重新编译放入缓存目录。如果没失效则直接读取输出或返回
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);
}
到这里整个流程就结束了,有兴趣的可以看下我另外一篇文章
tp6源码解析-第二天,ThinkPHP6编译模板流程详解,ThinkPHP6模板源码详解的更多相关文章
- Netty 源码解析(七): NioEventLoop 工作流程
原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第七篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源 ...
- Spring源码解析 | 第二篇:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析
一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...
- 【安卓网络请求开源框架Volley源码解析系列】定制自己的Request请求及Volley框架源码剖析
通过前面的学习我们已经掌握了Volley的基本用法,没看过的建议大家先去阅读我的博文[安卓网络请求开源框架Volley源码解析系列]初识Volley及其基本用法.如StringRequest用来请求一 ...
- Spring Security源码解析一:UsernamePasswordAuthenticationFilter之登录流程
一.前言 spring security安全框架作为spring系列组件中的一个,被广泛的运用在各项目中,那么spring security在程序中的工作流程是个什么样的呢,它是如何进行一系列的鉴权和 ...
- Apktool源码解析——第二篇
上一篇讲到ApkDecoder这个类,大部分调用到还是Androlib类,而且上次发现brutall的代码竟然不是最新的,遂去找iBotP.的代码了. 今天来看Androlib的代码: private ...
- Android View体系(八)从源码解析View的layout和draw流程
前言 上一篇文章我们讲了View的measure的流程,接下来我们讲下View的layout和draw流程,如果你理解了View的measure的流程,那这篇文章自然就不在话下了. 1.View的la ...
- mapbox.gl源码解析——基本架构与数据渲染流程
加载地图 Mapbox GL JS是一个JavaScript库,使用WebGL渲染交互式矢量瓦片地图和栅格瓦片地图.WebGL渲染意味着高性能,MapboxGL能够渲染大量的地图要素,拥有流畅的交互以 ...
- Redis源码解析:23sentinel(四)故障转移流程
十:故障转移流程中的状态转换 当哨兵针对某个主节点进行故障转移时,该主节点的故障转移状态master->failover_state,要依次经历下面六个状态: SENTINEL_FAILOVER ...
- Hadoop源码学习笔记之NameNode启动场景流程三:FSNamesystem初始化源码剖析
上篇内容分析了http server的启动代码,这篇文章继续从initialize()方法中按执行顺序进行分析.内容还是分为三大块: 一.源码调用关系分析 二.伪代码执行流程 三.代码图解 一.源码调 ...
随机推荐
- kubeasz部署高可用kubernetes1.17.2 并实现traefik2.1.2部署
模板机操作 # cat /etc/redhat-release CentOS Linux release 7.6.1810 (Core) # uname -a //内核升级到4.4.X以后, 关于如何 ...
- 基于 Roslyn 实现一个简单的条件解析引擎
基于 Roslyn 实现一个简单的条件解析引擎 Intro 最近在做一个勋章的服务,我们想定义一些勋章的获取条件,满足条件之后就给用户颁发一个勋章,定义条件的时候会定义需要哪些参数,参数的类型,获取勋 ...
- SpringCloud之Hystrix服务降级入门全攻略
理论知识 Hystrix是什么? Hystrix是由Netflix开源的一个服务隔离组件,通过服务隔离来避免由于依赖延迟.异常,引起资源耗尽导致系统不可用的解决方案.这说的有点儿太官方了,它的功能主要 ...
- Ubuntu下搭建.Net Core环境并发布MVC项目
支撑环境 1. Windows 10 1809 12月更新版本(其他版本应该也行,但建议不低于1809,过低的版本可能无法安装子系统ubuntu18.04 LTS) 2. ubuntu 18.04 L ...
- 阅读GitHub源码的正确打开方式
前言 近来发现阅读开源项目上手就整最新的代码不合适,缺少项目迭代的具体实现过程,想着若是可以看到针对问题的提交代码就好了,所以就有了本篇博客. 以文主要涉及:如何fork开源项目,如何保证本地仓库代码 ...
- 对tf.nn.softmax的理解
对tf.nn.softmax的理解 转载自律者自由 最后发布于2018-10-31 16:39:40 阅读数 25096 收藏 展开 Softmax的含义:Softmax简单的说就是把一个N*1的向 ...
- Mol Cell Proteomics. | A Targeted Mass Spectrometry Strategy for Developing Proteomic Biomarkers: A Case Study of Epithelial Ovarian Cancer(利用靶向质谱策略进行上皮性卵巢癌病例的蛋白质组生物标志物研究) (解读人:王聚)
文献名:利用靶向质谱策略进行上皮性卵巢癌病例的蛋白质组生物标志物研究 期刊名:Molecular & Cellular Proteomics 发表时间:(2019年7月) IF:5.41 单位 ...
- F版本SpringCloud 2—什么是SpringCloud?SpringCloud版本选择
引言:搭建微服务架构就像是买电脑,使用SpringCloud就是在买品牌机. 前言 昂,美好的天气里,不想直接说技术,给小伙伴萌看看傍晚的天空吧. -- 能找到天上的北极星吗? 上一篇文章中,通过一个 ...
- ysoserial分析【一】 之 Apache Commons Collections
目录 前言 基础知识 Transformer 利用InvokerTransformer造成命令执行 Map TransformedMap LazyMap AnnotationInvocationHan ...
- IOS抓包工具Stream——让移动端的抓包变得轻而易举
有一天下晚班回家,在地铁上的时候,开发发来信息说,能不能把之前创建的bug再抓包看下数据.顿时心里就想,在地铁上,我上哪抓包去.之后百度了下,发现ios有一款非常实用的抓包工具,大家可以上App St ...