ThinkPHP6 核心分析之应用程序初始化
runWithRequest () 方法
protected function runWithRequest(Request $request)
{
$this->initialize(); // 加载全局中间件
$this->loadMiddleware();
.
.
.
protected function initialize()
{
//如果还未初始化,则初始化之
if (!$this->app->initialized()) {
$this->app->initialize();
}
}
public function initialize()
{
// 设置应用状态为已经初始化
$this->initialized = true; //记录开始时间
$this->beginTime = microtime(true);
//记录起始内存使用量
$this->beginMem = memory_get_usage(); // ==( A )== 加载环境变量
// $this->env跟前面的(new App())->http和$this->config都是同样的套路
if (is_file($this->rootPath . '.env')) {
$this->env->load($this->rootPath . '.env');
}
//设置配置文件后缀
$this->configExt = $this->env->get('config_ext', '.php');
// ==( B )== 设置调试模式
$this->debugModeInit(); // ==( C )== 加载应用文件和配置等操作
$this->load(); // 加载框架默认语言包
$langSet = $this->lang->defaultLangSet();
// 框架目录下对应的语言包
// 比如:D:\dev\tp6\vendor\topthink\framework\src\lang\zh-cn.php
$this->lang->load($this->thinkPath . 'lang' . DIRECTORY_SEPARATOR . $langSet . '.php'); // 加载应用默认语言包
// 这个会扫描「app/lang」里面,对应语言包文件夹的所有「.php」文件
// 比如,app/lang/zh-cn/* 下的所有文件
// 然后加载解析
$this->loadLangPack($langSet); // 监听AppInit
$this->event->trigger('AppInit'); // 设置时区
date_default_timezone_set($this->config->get('app.default_timezone', 'Asia/Shanghai')); // ==( D )== 初始化
// 初始化错误和异常处理、注册系统服务和初始化系统服务
foreach ($this->initializers as $initializer) {
$this->make($initializer)->init($this);
} return $this;
}
(A) 加载环境变量
public function load(string $file): void
{
$env = parse_ini_file($file, true) ?: [];
$this->set($env);
}
public function set($env, $value = null): void
{
if (is_array($env)) {
//全部KEY转为大写字母
$env = array_change_key_case($env, CASE_UPPER); foreach ($env as $key => $val) {
//有二级配置的,转为KEY1_KEY2 => $v 的形式
if (is_array($val)) {
foreach ($val as $k => $v) {
$this->data[$key . '_' . strtoupper($k)] = $v;
}
} else {
$this->data[$key] = $val;
}
}
//ENV的值不是数组的情况
} else {
$name = strtoupper(str_replace('.', '_', $env)); $this->data[$name] = $value;
}
}


(B) 调试模式设置
protected function debugModeInit(): void
{
// 应用调试模式
if (!$this->appDebug) {
$this->appDebug = $this->env->get('app_debug') ? true : false;
// 关闭错误显示
ini_set('display_errors', 'Off');
}
// 如果不是命令行模式
if (!$this->runningInConsole()) {
// 重新申请一块比较大的buffer
// php缓冲控制
// 参考:https://www.php.net/manual/en/ref.outcontrol.php
// https://www.cnblogs.com/saw2012/archive/2013/01/30/2882451.html
// 新版PHP默认开启缓冲区ob_start(),ob_get_level() == 1
if (ob_get_level() > 0) {
// 相当于ob_get_contents() 和 ob_clean()
// 获取缓冲区内容并删除缓冲区内容
$output = ob_get_clean();
}
// 开启新的缓冲区控制
ob_start();
if (!empty($output)) {
// 由于开启了新的缓冲区控制,
// 这里不会直接输出$output
// 而是等到依次执行了ob_flush()和flash()之后才将内容输出到浏览器
echo $output;
}
}
}
(C) 加载应用文件和配置等操作
protected function load(): void
{
$appPath = $this->getAppPath(); // 加载「app」目录下的「common.php」文件
if (is_file($appPath . 'common.php')) {
include_once $appPath . 'common.php';
}
// 加载核心目录下的「helper.php」文件
// 可以看到,这里的加载顺序:先「common.php」,后「helper.php」
// 且「helper.php」中的函数包裹在「if (!function_exists('xxx'))」下
// 所以可以在「common.php」文件中覆盖系统定义的助手函数
include_once $this->thinkPath . 'helper.php'; $configPath = $this->getConfigPath(); $files = []; // glob的作用是扫描给定路径模式下的文件,非常好用
// 这里扫描「config」目录下的所有「.php」文件
if (is_dir($configPath)) {
$files = glob($configPath . '*' . $this->configExt);
} foreach ($files as $file) {
// $this->config 还是同样的套路获得了「Config」类的实例
// 「load」的第二个参数为一级配置名,这里传入一个文件名,所有文件名作为一级配置
// 比如「app.php」配置文件,一级配置为「app」
// 在 「Config」类作用域下的操作:
// 「load」加载文件后,通过「parse」方法解析文件内容
// 最后,通过「set」方法将所有配置合并了「config」成员变量
$this->config->load($file, pathinfo($file, PATHINFO_FILENAME));
} // 加载「app」目录下的「event.php」文件
if (is_file($appPath . 'event.php')) {
$this->loadEvent(include $appPath . 'event.php');
}
// 注册自定义的服务
if (is_file($appPath . 'service.php')) {
$services = include $appPath . 'service.php';
foreach ($services as $service) {
$this->register($service);
}
}
}
(D) 初始化错误和异常处理、注册系统服务和初始化系统服务
foreach ($this->initializers as $initializer) {
$this->make($initializer)->init($this);
}
protected $initializers = [
Error::class, //错误处理类
RegisterService::class, //注册系统服务类
BootService::class, //启动系统服务
];
protected $services = [
PaginatorService::class,
ValidateService::class,
ModelService::class,
];
foreach ($services as $service) {
if (class_exists($service)) {
// 注册到系统服务
$app->register($service);
}
}
class BootService
{
public function init(App $app)
{
$app->boot();
}
}
public function boot(): void
{
array_walk($this->services, function ($service) {
$this->bootService($service);
});
}
public function bootService($service)
{
if (method_exists($service, 'boot')) {
return $this->invoke([$service, 'boot']);
}
}
- 系统自带的,如 PaginatorService,ValidateService,ModelService;
- app 目录下,在「service.php」文件中自定义的;
- vendor 目录下的「service.php」文件定义的。

ThinkPHP6 核心分析之应用程序初始化的更多相关文章
- ThinkPHP6 核心分析:系统服务
什么是系统服务?系统服务是对于程序要用到的类在使用前先进行类的标识的绑定,以便容器能够对其进行解析(通过服务类的 register 方法),还有就是初始化一些参数.注册路由等(不限于这些操作,主要是看 ...
- ThinkPHP6 核心分析之Http 类跟Request类的实例化
以下源码分析,我们可以从 App,Http 类的实例化过程,了解类是如何实现自动实例化的,依赖注入是怎么实现的. 从入口文件出发 当访问一个 ThinkPHP 搭建的站点,框架最先是从入口文件开始的, ...
- 《深入理解Spark:核心思想与源码分析》——SparkContext的初始化(叔篇)——TaskScheduler的启动
<深入理解Spark:核心思想与源码分析>一书前言的内容请看链接<深入理解SPARK:核心思想与源码分析>一书正式出版上市 <深入理解Spark:核心思想与源码分析> ...
- 01-Coredump核心转存&&Linux程序地址分析【转】
转自:http://www.itwendao.com/article/detail/404132.html 目录(?)[-] 一Core Dump核心转存 二Linux程序地址分析 一Core Dum ...
- 【spring源码分析】IOC容器初始化(十一)
前言:前面分析了doCreateBean中的createBeanInstance函数,接下来分析其剩余流程. 首先贴上doCreateBean函数: // AbstractAutowireCapabl ...
- Envoy 源码分析--程序启动过程
目录 Envoy 源码分析--程序启动过程 初始化 main 入口 MainCommon 初始化 服务 InstanceImpl 初始化 启动 main 启动入口 服务启动流程 LDS 服务启动流程 ...
- 【spring源码分析】IOC容器初始化——查漏补缺(五)
前言:我们知道在Spring中经常使用配置文件的形式对进行属性的赋值,那配置文件的值是怎么赋值到属性上的呢,本文将对其进行分析. 首先了解一个类:PropertySourcesPlaceholderC ...
- 在 NetBeans IDE 6.0 中分析 Java 应用程序性能
NetBeans IDE 6.0 包含一个强大的性能分析工具,可提供与应用程序运行时行为有关的重要信息.通过 NetBeans 性能分析工具,我们可以方便地在 IDE 中监控应用程序的线程状态.CPU ...
- 【spring源码分析】IOC容器初始化(总结)
前言:在经过前面十二篇文章的分析,对bean的加载流程大致梳理清楚了.因为内容过多,因此需要进行一个小总结. 经过前面十二篇文章的漫长分析,终于将xml配置文件中的bean,转换成我们实际所需要的真正 ...
随机推荐
- 深入Mybatis源码——配置解析
@ 目录 前言 正文 配置解析 1. cacheRefElement/cacheElement 2. resultMapElements 3. sqlElement 4. buildStatement ...
- 记录一下安装hexo的过程
记录一下安装hexo的过程 首先你的电脑需要安装node.js和Git 安装好Git之后需要配置本机与Github之间的ssh方便更新同步博客到Github上,在一个地方新建一个文件夹作为我们博客的根 ...
- Tomcat 架构原理解析到架构设计借鉴
Tomcat 发展这么多年,已经比较成熟稳定.在如今『追新求快』的时代,Tomcat 作为 Java Web 开发必备的工具似乎变成了『熟悉的陌生人』,难道说如今就没有必要深入学习它了么?学习它我们又 ...
- 深入Vue-router最佳实践
前言 最近再刷Vue周边生态的官方文档,因为之前的学习都是看视频配合着文档,但主要还是通过视频学习,所以很多知识点都没有了解,至从上次刷了Vuex的官方文档就体会到了通读文档的好处,学习一门技术最好的 ...
- scrapy 基础组件专题(十四):scrapy CookiesMiddleware源码
一 Scrapy框架--cookie的获取/传递/本地保存 1. 完成模拟登陆2. 登陆成功后提取出cookie,然后保存到本地cookie.txt文件中3. 再次使用时从本地的cookie.txt中 ...
- bzoj3196Tyvj1730二逼平衡树
bzoj3196Tyvj1730二逼平衡树 题意: 维护一个数列,操作:查询k在区间内的排名.查询区间内排名为k的值3.修改某一位上的数值.查询k在区间内的前驱(前驱定义为小于x,且最大的数).查询k ...
- Quartz.Net系列(十六):Misfire策略在SimpleScheduler和CronScheduler中的使用
1.场景 ①因为工作线程都在忙碌,所以导致某些Trigger得不到触发 也就是默认10个工作线程而我有15个Trigger同时触发 这就导致有5个不能被触发,而不幸的是Trigger所关联的Job执行 ...
- JVM系列之:String,数组和集合类的内存占用大小
目录 简介 数组 String ArrayList HashMap HashSet LinkedList treeMap 总结 简介 之前的文章中,我们使用JOL工具简单的分析过String,数组和集 ...
- 基于Vue的UI框架element el-table表格的自定义排序
html部分: <el-table-column prop="phoneCache" label="手机缓存包编号" align="center ...
- 检查string是否有重复尝试用map
链接: 题意:输入一些单词,找出所有满足如下条件的单词:该单词不能通过字母重排,得到输入文本的另外一个单词.在判断是否满足条件时,字母不分大小写,但在输出时应保留输入的大小写,按字典序排列. 题解:先 ...