thinkphp6 源碼分析之應(yīng)用初始化
App Construct
先來看看在 __construct 中做了什么,基本任何框架都會(huì)在這里做一些基本的操作,也就是從這里開始延伸出去。
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('thinkContainer',?$this); }
● 從魔術(shù)的方法的參數(shù) rootPath 來看,是支持自定義根目錄路徑的。
●?設(shè)置了 thinkPath, rootPath, appPath, runtimePath
立即學(xué)習(xí)“PHP免費(fèi)學(xué)習(xí)筆記(深入)”;
●?綁定了默認(rèn)的服務(wù)提供者,一共提供了兩個(gè),appReques 和 appExceptionHandle,實(shí)際上你使用的 Request 就是它。具體到 appPath 查看
●?設(shè)置當(dāng)前容器實(shí)例 APP
●?將 App($this) 實(shí)例 綁定到容器中,分別是 app 和 thinkContainer
這里需要注意的是 App 類是繼承 Container 的,所以就是將自身實(shí)例綁定到容器中。
在這里似乎整個(gè)應(yīng)用就已經(jīng)初始化結(jié)束了?這里我需要把一部分 Request run 的內(nèi)容放在這里說,因?yàn)槟抢锊攀强蚣苤饕某跏蓟ぷ鳎也⒉徽J(rèn)為將這一部分初始化工作放在 Request run 中是合理的。
主要的初始化
public?function?initialize() { ????$this->initialized?=?true; ????$this->beginTime?=?microtime(true); ????$this->beginMem??=?memory_get_usage(); ????//?加載環(huán)境變量 ????if?(is_file($this->rootPath?.?'.env'))?{ ????????$this->env->load($this->rootPath?.?'.env'); ????} ????$this->configExt?=?$this->env->get('config_ext',?'.php'); ????$this->debugModeInit(); ????//?加載全局初始化文件 ????$this->load(); ????//?加載框架默認(rèn)語言包 ????$langSet?=?$this->lang->defaultLangSet(); ????$this->lang->load($this->thinkPath?.?'lang'?.?DIRECTORY_SEPARATOR?.?$langSet?.?'.php'); ????//?加載應(yīng)用默認(rèn)語言包 ????$this->loadLangPack($langSet); ????//?監(jiān)聽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 環(huán)境變量文件
●?加載配置文件以及應(yīng)用內(nèi)的文件
? ? ●?加載應(yīng)用內(nèi)的 common.php
? ? ●?加載助手函數(shù) 在 thinkPath 目錄下的 helper.php
●?加載配置文件
●?加載應(yīng)用目錄下的 event.php 事件
●?注冊(cè)應(yīng)用目錄下的 service.php 服務(wù)
●?加載語言包
●?監(jiān)聽 AppInit 事件,利用該事件可以做一些請(qǐng)求前的工作
●?設(shè)置時(shí)區(qū)
●?注入所有服務(wù)并且啟動(dòng)服務(wù)
服務(wù)注冊(cè)
初始化過程中,進(jìn)行服務(wù)注冊(cè),那么服務(wù)注冊(cè)做了哪些事情呢?該如何使用的服務(wù)呢?
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; }
●?服務(wù)是否注冊(cè)過,如果需要強(qiáng)制重新注冊(cè)
●?實(shí)例化服務(wù)
●?如果實(shí)現(xiàn)了 register 方法,則需要執(zhí)行 register 方法
●?如果設(shè)置了 bind 屬性,則需要將 service 實(shí)例綁定到容器
●?最后合并到整個(gè) service 數(shù)組中,等待 boot
服務(wù)啟動(dòng)
目前在初始化的時(shí)候只有下面三個(gè)服務(wù),在 $this->initializers 數(shù)組中
foreach?($this->initializers?as?$initializer)?{ ????????$this->make($initializer)->init($this); }
這三個(gè)服務(wù)分別是:
thinkinitializerBootService thinkinitializerError thinkinitializerRegisterService
● Error 服務(wù)是用來處理框架異常和錯(cuò)誤的
●?RegisterService 從字面的意思就是注冊(cè)服務(wù)的
●?BootService 就是啟用服務(wù)的
Error 處理在之后再說,這里說一下 RegisterService 和 BootService。
當(dāng)從 Container 中 make 出 RegisterService 的時(shí)候
這里有個(gè)隱藏的靜態(tài)方法 make,每次如果首次從 Container 中 make 出來的實(shí)例對(duì)象都會(huì)執(zhí)行 make 方法,當(dāng)然首先必須你實(shí)現(xiàn)了該方法。
隨后會(huì)執(zhí)行 Init 方法。當(dāng)你進(jìn)入到 RegisterService 的時(shí)候,你會(huì)看到該方法。方法內(nèi)容如下:
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); ????????} ????} }
該方法就很奇怪了,和我想象的有點(diǎn)不一樣。服務(wù)是直接從 runtime 目錄下面獲取的,而非在 config 目錄下的 service.php 中。為什么會(huì)這樣呢?由于 composer 的發(fā)展,TP 框架也可以提供包的自動(dòng)發(fā)現(xiàn)的功能,這也證明了開發(fā)組在不斷向社區(qū)靠攏。下面來看一下是如何實(shí)現(xiàn)的。
因?yàn)檫@都是得益于 composer 的,所以來看一下 rootPath 下的 composer.json,到最下面,你會(huì)發(fā)現(xiàn)下面的配置
"scripts":?{ ????"post-autoload-dump":?[ ????????"@php?think?service:discover", ????????"@php?think?vendor:publish" ????] }
從配置來看,框架一共提供了兩個(gè)指令,service:discover 和 vendor:publish。具體實(shí)現(xiàn)這里就不說了,你只需要知道包的發(fā)現(xiàn)是由 service:discover 實(shí)現(xiàn)的。
還有就是這里默認(rèn)注入了三個(gè)服務(wù)。
PaginatorService::class, ValidateService::class, ModelService::class,
最后再來看看 BootService,這個(gè)就很簡(jiǎn)單了。從命名來講就不難看出,下面就是代碼,正常的啟動(dòng)服務(wù),但是這里要說明的是,服務(wù)類中必須實(shí)現(xiàn)了 boot 方法才會(huì)啟動(dòng)。
public?function?init(App?$app) { ????$app->boot(); }