App Construct
先來看看在 __construct 中做了什麼,基本任何框架都會在這裡做一些基本的操作,也就是從這裡開始延伸出去。
public function __construct(string $rootPath = '')
{
$this->thinkPath = dirname(__DIR__) . DIRECTORY_SEPARATOR;
$this->rootPath = $rootPath ? rtrim($rootPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : $this->getDefaultRootPath();
$this->appPath = $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
$this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
if (is_file($this->appPath . 'provider.php')) {
$this->bind(include $this->appPath . 'provider.php');
}
static::setInstance($this);
$this->instance('app', $this);
$this->instance('think\Container', $this);
}
- 從魔術的方法的引數
rootPath
來看,是支援自定義根目錄路徑的。 - 設定了
thinkPath
,rootPath
,appPath
,runtimePath
- 繫結了預設的服務提供者,一共提供了兩個,
app\Reques
和app\ExceptionHandle
,實際上你使用的Request
就是它。具體到appPath
檢視 - 設定當前容器例項
APP
- 將
App($this)
例項 繫結到容器中,分別是app
和think\Container
這裡需要注意的是 App 類是繼承 Container 的,所以就是將自身例項繫結到容器中。
在這裡似乎整個應用就已經初始化結束了?這裡我需要把一部分 Request run
的內容放在這裡說,因為那裡才是框架主要的初始化工作,我並不認為將這一部分初始化工作放在 Request run
中是合理的。
主要的初始化
public function initialize()
{
$this->initialized = true;
$this->beginTime = microtime(true);
$this->beginMem = memory_get_usage();
// 載入環境變數
if (is_file($this->rootPath . '.env')) {
$this->env->load($this->rootPath . '.env');
}
$this->configExt = $this->env->get('config_ext', '.php');
$this->debugModeInit();
// 載入全域性初始化檔案
$this->load();
// 載入框架預設語言包
$langSet = $this->lang->defaultLangSet();
$this->lang->load($this->thinkPath . 'lang' . DIRECTORY_SEPARATOR . $langSet . '.php');
// 載入應用預設語言包
$this->loadLangPack($langSet);
// 監聽AppInit
$this->event->trigger('AppInit');
date_default_timezone_set($this->config->get('app.default_timezone', 'Asia/Shanghai'));
// 初始化
foreach ($this->initializers as $initializer) {
$this->make($initializer)->init($this);
}
return $this;
}
- 載入 .env 環境變數檔案
- 載入配置檔案以及應用內的檔案
- 載入應用內的 common.php
- 載入助手函式 在
thinkPath
目錄下的helper.php
- 載入配置檔案
- 載入應用目錄下的 event.php 事件
- 註冊應用目錄下的 service.php 服務
- 載入語言包
- 監聽 AppInit 事件,利用該事件可以做一些請求前的工作
- 設定時區
- 注入所有服務並且啟動服務
服務註冊
初始化過程中,進行服務註冊,那麼服務註冊做了哪些事情呢?該如何使用的服務呢?
public function register($service, bool $force = false)
{
$registered = $this->getService($service);
if ($registered && !$force) {
return $registered;
}
if (is_string($service)) {
$service = new $service($this);
}
if (method_exists($service, 'register')) {
$service->register();
}
if (property_exists($service, 'bind')) {
$this->bind($service->bind);
}
$this->services[] = $service;
}
- 服務是否註冊過,如果需要強制重新註冊
- 例項化服務
- 如果實現了
register
方法,則需要執行register
方法 - 如果設定了
bind
屬性,則需要將service
例項繫結到容器 - 最後合併到整個
service
陣列中,等待boot
服務啟動
目前在初始化的時候只有下面三個服務,在 $this->initializers
陣列中
foreach ($this->initializers as $initializer) {
$this->make($initializer)->init($this);
}
這三個服務分別是:
think\initializer\BootService
think\initializer\Error
think\initializer\RegisterService
Error
服務是用來處理框架異常和錯誤的RegisterService
從字面的意思就是註冊服務的BootService
就是啟用服務的
Error
處理在之後再說,這裡說一下 RegisterService
和 BootService
。
當從 Container
中 make
出 RegisterService
的時候
這裡有個隱藏的靜態方法 make,每次如果首次從 Container 中 make 出來的例項物件都會執行 make 方法,當然首先必須你實現了該方法。
隨後會執行 Init 方法。當你進入到 RegisterService
的時候,你會看到該方法。方法內容如下:
public function init(App $app)
{
$file = $app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . 'services.php';
$services = $this->services;
if (is_file($file)) {
$services = array_merge($services, include $file);
}
foreach ($services as $service) {
if (class_exists($service)) {
$app->register($service);
}
}
}
該方法就很奇怪了,和我想象的有點不一樣。服務是直接從 runtime
目錄下面獲取的,而非在 config
目錄下的 service.php
中。為什麼會這樣呢?由於 composer 的發展,TP 框架也可以提供包的自動發現的功能,這也證明了開發組在不斷向社群靠攏。下面來看一下是如何實現的。
因為這都是得益於 composer 的,所以來看一下 rootPath
下的 composer.json,到最下面,你會發現下面的配置
"scripts": {
"post-autoload-dump": [
"@php think service:discover",
"@php think vendor:publish"
]
}
從配置來看,框架一共提供了兩個指令,service:discover
和 vendor:publish
。具體實現這裡就不說了,你只需要知道包的發現是由 service:discover
實現的。
還有就是這裡預設注入了三個服務。
PaginatorService::class,
ValidateService::class,
ModelService::class,
最後再來看看 BootService
,這個就很簡單了。從命名來講就不難看出,下面就是程式碼,正常的啟動服務,但是這裡要說明的是,服務類中必須實現了 boot
方法才會啟動。
public function init(App $app)
{
$app->boot();
}
以上就是整個應用的初始化,具體細節在之後討論
原文轉載與 thinkphp6原始碼分析之應用初始化
本作品採用《CC 協議》,轉載必須註明作者和本文連結