❝在上期聊了ThinkPHP類的自動加載,如你還不太了解可以跟這下文鏈接去進行查看。本文會帶你一起解讀ThinkPHP配置文件。
❞
前言
想了很久終於要開始系列文章的編寫了,期望是寫出提升和面試都可以搞定的系列文章。
當你看到本文時,如果你發現咔咔沒有編寫到的面試熱點問題或者技術難點,期待評論區指出,一起完善。
-
第一期文章: ThinkPHP自動加載Loader源碼分析 -
第二期文章: ThinkPHP配置源碼分析
一、配置文件的種類

在ThinkPHP中有四類配置文件,你知道多少呢!不知道也沒關系咔咔帶你在看一次。
這四種配置文件分別為慣例配置、應用配置、模塊配置、動態配置。
在這里需要說明一下,一般開發中慣例配置和動態配置是不會去使用的,尤其動態配置更不會去使用。
動態配置是直接使用Config::set("設置動態配置文件")來進行設置的,所以這個東西不要去用,一定不要去用,否則項目后期怎么去管理。
關於這四類配置文件咔咔直接用一幅圖來給大家展示,就不過多說明了,本文的重點不是給大家介紹這玩意。
關於ThinkPHP5.1取消了單獨的config文件,將應用配置修改到config下的app.php文件中。
新增了模塊配置,模塊配置也是在config目錄下,例如你在app目錄下有倆個模塊,分別為index、admin這倆個模塊,那么就可以直接在config目錄下創建admin、index目錄,然后創建對應模塊的配置文件即可。
這四種配置文件的優先級就是上圖的排列順序,管理配置、應用配置、模塊配置、動態配置
二、學習使用ArrayAccess
在文件thinkphp/library/think/Config.php
,類Config實現了一個接口為ArrayAccess
。
這個時候你是不是有疑問了,這個類到底是干嘛的,為什么要去了解和學習它!帶着這個疑問繼續往下探尋答案吧!
到這個接口里邊一探究竟。
在這個接口里邊有四個接口需要實現分別為
-
offsetExists 檢測偏移位置是否存在 -
offgetGet 獲取一個偏移位置的值 -
offsetSet 設置一個偏移位置的值 -
offsetUnset 刪除一個偏移位置的值
這幾個函數放到這是不是有點懵呢!別着急,這就給你解答
這幾個方法在Config中也進行實現,但是里邊使用了幾個方法,根據上邊對方法的作用說明后。
像set、has、remove、get想想大家就知道是什么意思了。

接下來,咱們自己來實現一下這個類,給大家演示一下這個類用處到底是什么。ArrayAccess這個類不僅是TP大量使用,在laravel中也是大量存在,所以需要好好學習這個類的作用和思想。
需要在kaka目錄下創建一個文件TestArrayAccess
文件,並且設置一個屬性,在實現ArrayAccess類。
至於我自己創建的這個文件夾kaka
為什么會執行自動加載就是上一期在類的自動加載中實現的。
如若不會的話可以把文件先放置到extend目錄下即可。
接着來到
application/index/controller/Index.php
控制器使用上圖實現的方法

打印結果 這里在使用
offsetGet
打印的結果為kaka
應該都明白怎么回事了吧!其余倆個方法就不去演示了,相信你也已經明白了。
所以說這個ArrayAccess類就是提供像訪問數組一樣訪問對象的接口
。
三、了解Yaconf
估計有同學知道Yaconf就是我們牛逼克拉斯的鳥哥寫的。
咔咔了解Yaconf后,總結推出這個開源的幾點原因。
-
配置文件過多,導致加載時間過長 -
配置文件可讀性差,需要運行解析 -
配置文件與代碼同屬一個項目部署在一起會有安全隱患,同時如果配置文件修改時還需要走上線流程 -
加大運維與開發協同難度,如果運維需要修改MySQL或者其它配置也需要通知開發進行同步修改
那么在來說一下使用Yaconf的優點
-
不跟代碼在一起,使用專屬的配置目錄 -
PHP啟動時,加載完所有的配置,進行常駐內存,伴隨着PHP的生命周期存亡,避免每次請求時解析配置文件,消耗時間 -
配置文件跟代碼分離,就可以借助配置后台管理來統一管理配置信息 -
配置文件如有發生變化時會進行重載(這里給的建議是使用mv而非cp) -
支持類型豐富,例如:字符串、數組、分節、並且還可以在PHP配置文件里邊直接使用PHP的環境變量和常量 -
最后一點就是使用非常簡單
在了解了Yaconf可以做什么的時候之后,在去開始下一步的操作,別一上來就是安裝、配置、調用,然后一頓操作結束后,不知道是干嘛的,那樣就沒有任何意義了。
接下來將會介紹在win、linux上安裝並且使用
四、Yaconf在window上安裝
點擊后邊的DLL,選擇對應的版本
選擇適用的版本
如何判斷什么版本適合自己,對照着我圈起來的幾個地方去下載就對了
然后將這個文件放置到PHP的擴展目錄里邊
修改配置文件
extension=php_yaconf.dll
[yaconf]
yaconf.directory="D:\phpstudy_pro\WWW\yaconf"
yaconf.check_delay=60
重啟環境,然后查看一下就ok了。
這里需要注意一點就是在復制的時候難免會有一些空格什么亂七八糟的東西,切記刪除干凈,否則你會知道什么叫難以啟齒的柔弱。
五、Yaconf在window上使用
此時就直接在Yaconf的目錄中創建一個ini文件,並且寫上如下內容
控制器直接讀取
經過打印出來的結果也是預期的值
文件如有改動,則直接重啟ngixn即可
Yaconf提供的另外的一個接口為
\Yaconf::has
以上就是Yaconf在window上的使用,使用起來還是相對簡單的。
這里有一個注意點就是修改了Yaconf的配置文件之后我們需要重啟web服務器。
六、Config源碼剖析
雖說上面的Yaconf對於config的源碼解析沒有多大的幫助,但是也是在擴寬一下思路,以后在工作中可以嘗試使用Yaconf。
進入正題,想知道Config是怎么進行加載解析的,先來畫一個圖。一起看一下加載config的執行流程。

回到public/index.php
,在上一期說了類的自動加載是在加載base.php文件的過程中執行的。
那么config的加載是在下圖框起來的這里,這里涉及到了容器,會有一個單獨的專題來對容器進行剖析。
這里就不過多說明了,這段代碼回去執行`D:\phpstudy_pro\WWW\ThinkPHPSourceCodeAnalysis\thinkphp\library\think\App.php這個文件的run方法。
並且在
run
方法中需要追蹤的是initialize
應用初始化這個函數
接着在
initialize
這個方法中就會看到配置文件的蛛絲馬跡,隨之而來就是一個初始化應用init
直到走到init方法中,才算是進入了主題。
開始了自動加載配置文件,並且還調用了config類中的load方法,也是需要一起閱讀的。
來到這里之后需要簡單的進行解讀一下
這段代碼會直接走到elseif中,因為在app目錄下沒有設置config目錄
並且這里有個configPath這個屬性熟悉吧!這個值最終就是D:\phpstudy_pro\WWW\ThinkPHPSourceCodeAnalysis\config\
在接這就是把config目錄下的文件全部拿出來。
傳遞給config類的load方法
在這里有幾個知識點提一下,就當回顧了
-
scandir :以升序的方式返回一個目錄下的所有文件,還有第二個參數1,表示降序的方式返回一個目錄下的所有文件。 -
pathinfo:以數組的形式返回文件信息,分別為目錄名、文件名、擴展名,其中的幾個參數代碼中有提到,可以看下圖即可。
在這段代碼中有一個屬性configExt,這個值是在環境變量讀出來的,給的值是php
緊接着就需要來到
thinkphp/library/think/Config.php
這個文件了,在app文件中最后調用了config類中的load。
一起來看看都經歷了什么
在load這個方法中,流程的最終走向會到loadFile
這個方法中,至於elseif的代碼為什么不會走,或者說這段代碼就是多余的。
因為當Yaconf安裝后在PHP啟動后就會直接去加載對應的配置文件
來到loadFile這個文件后
我們都知道在ThinkPHP框架中,config目錄下的所有文件都是PHP類型的
所以在判斷了類型后就直接進入到set里邊進行數據的處理
include直接引入的就是config目錄下的配置文件,並且所有的配置文件都是直接return返回一個數組
在set方法中,這塊代碼就是核心了
一直循環合並數組,最終把所有的配置信息都返回給了config這個屬性
截止到這里config目錄下的所有配置就加載完成了。
以上就是config的加載流程,其實當你閱讀完之后感覺也沒有那么的難,就是編碼技巧和思想。
而我們閱讀源碼不是看它代碼怎么寫的,是學習的它的編碼思想,最終落地到我們自己的項目中。
七、工廠模式加載其它類型的配置文件
在之前看到的loadFile方法中,文件類型為PHP或者yaml程序就打斷了,就不會在去執行后邊的pares方法。
那么這個parse方法是做什么的呢!
進入到paras這個方法后,首先看注釋
隨后使用了一個工廠模式去加載think/config/driver下的文件
進入到factory這個方法后,這塊內容屬於容器的就不過多解釋了。
只需要知道這里最終會返回一個實例給paras
方法的$object變量
最終還是使用在上文中提到的set方法,而內部的object->parse()就是執行返回對象的內部方法,例如下圖展示三個類型。ini、json、xm三個類型都存在同樣的方法
這是ini類型,其它倆個的類型也是一毛一樣的,就是會有同樣的方法來實現對應的功能
「簡單梳理一下工廠模式加載不同類型的配置」
-
把對應的類型傳給一個方法 -
然后這個方法返回對應的實例 -
在用這個實例去調用類里邊的方法 -
所有實例里的方法名都是一致的
這里咔咔后期會單獨出一篇文章模仿這個實現另一個功能,導圖會有所有的文章鏈接哦!
在ini.php中看到了一個方法parse_ini_file
,其實這個方法根據之前的學習就大概能了解到時把ini類型的配置文件轉為數組形式。這里就不做演示了,很是簡單哈!
那么其它倆種類型都是為了把文件數據轉為數組的。
八、yaml初體驗
yaml就是一個類似於xml、json數據通信方式,但是yaml是以數據為中心,而非標記語言為重點。
這里提到yaml是因為在框架源碼中提到了這個,后邊的流程也是需要走yaml,所以還是需要簡單的了解一下哈!
安裝yaml,直接到pcel里搜索yaml,下載對應的版本即可。下載方式跟之前yaconf安裝一樣的。
安裝成功后就會在PHP中存在這個擴展。
這里需要注意的是需要在php.ini中把yaml擴展文件加進去哈!
如果不會就去Yaconf在window上安裝那一欄去看是怎么安裝的,安裝那個流程就可以了。
安裝完成后就是簡單的使用了,在config中新建一個kaka.yaml文件。
並且寫上如下的內容,切記在yaml中冒號后邊需要空一格,yaml寫法就不過多說了,畢竟平時不怎么用。
這里說明只是為了閱讀框架代碼而做的工作。
測試yaml數據讀取
讀取出來的數據就是一個數組,也就是說把yaml格式的數據轉化為數組形式。
這個方法是從哪里知道的呢!
還記得在config類下loadFile方法中,根據文件擴展的不同加載不同形式的方法。
PHP類型的直接就走了set,yaml類型的把數據處理為數組后執行set方法
如果是其它類型的話就會在上邊說的工廠模式返回對應類名的實例,並執行對應類里邊的方法將格式都轉化為數組形式。最終還是使用set方法
其它類型的配置文件最終都會走到set方法里邊。

九、如何讓框架加載其它類型的配置文件
在初始化應用和模塊中有一個屬性是configExt,這個屬性就是文件的擴展
去找一下這個屬性是在哪里設置的。
根據config配置文件加載流程,可以很清晰的看到init
方法的上一層是初始化應用,也就是initialize
方法。
那么這個屬性肯定是在init
方法之前就已經提前設定好了的。
返回到init
方法的上一層initialize
就直接可以看到這個值的設定。

這個值是從env的環境變量中獲取的,如果沒有則默認為php,所以就需要創建一個env的文件。
並且給一個默認值為yaml
這里給大家看一下變化,在5.1.34 LTS版本的時候存在一個bug,咔咔目前使用的是5.1.39 LTS 這個問題已經修復了。
這段代碼相信都可以看出來,它是先對configExt進行了設置默認值,然后在去加載環境變量配置文件。
那么加載這個環境變量的這段代碼就毫無任何意義,configExt的值永遠都是.php
就算在env文件里邊做了配置也不會獲取得到。
在之前在config配置文件中添加了一個yaml的配置文件
那么這個時候就可以使用config類來獲取yaml類型文件的配置了。
測試一下沒有任何問題,數據是可以出來的。
但是實際項目中可不敢這么整啊!如果要把configExt
這個值配置到環境變量,配置的是什么類型就需要把config目錄下的所有配置文件全部轉化為對應類型。
例如configExt設置的為.ini ,則就需要把config目錄下的所有文件都改為ini文件形式
這塊內容只是針對閱讀源碼后一個運用而已,實際項目中不要這樣使用,因為在tp框架中所有的配置文件都是PHP類型的。
如果改為其它類型的話,就需要修改框架中所有的配置文件,這種事情能不干就不哈!
十、框架底層配置加載代碼優化
在thinkphp/library/think/Config.php
中方法loadFile這里看起來是不是有點不太優雅了。
既然在方法最后使用parse
方法,也就是之前提到的工廠模式加載其它類型的配置文件。
那么為何不讓這個工廠模式也加載PHP和yaml類型的配置文件。
此時就需要在
thinkphp/library/think/config/driver
這個目錄建立php文件和yaml文件了。
首先建立一個php文件。
並且仿照其它三個類型文件,在php文件類型中只需要判斷是否為文件,然后把文件引入進來即可。
在框架中PHP類型的配置文件都是數組形式的,所以在Php.php文件中parse方法直接返回config屬性即可。
開始開心的測試吧!之前在env的文件中配置了CONFIG_EXT這個值為yaml。
也就是說讀取config目錄下的配置文件時,只能讀取擴展為yaml類型的文件。
所以需要先把這個值給改回來,等后面把yaml類型的工廠類寫好之后就可以使用了。
為了測試方便在config目錄下添加一個新的配置
然后把config類中的lodeFile方法中判斷php和yaml類型的代碼注釋掉

在控制器讀取config/app.php配置
打印結果
打印出來的結果沒有任何瑕疵,也就說我們進行簡單優化的代碼並沒有不適之處。 至於yaml也是一樣的道理,只需要把最終的數據轉為數組返回就行了。
以上就是咔咔對框架配置文件加載底層源碼優化的解析過程,如有不適之處,可以評論區指出來。
十一、解析如何獲取config如何獲取配置
都知道在獲取配置信息的時候直接使用\Config::get()
就可以獲取到配置文件的信息。
接下來咔咔就來剖析一下獲取配置的流程。
框架給提供了幾個方法來獲取配置信息。
-
\Config::get('配置參數'); -
\Config::get('配置文件'); -
\Config::pull('配置文件');
這其中估計使用第一種的就很少了,第一種的方式就是直接獲取所有配置文件中的對應的配置。
例如:想獲取config目錄下的應用名稱配置
就可以直接用\Config::get('app_name');來直接獲取
那么這個流程是怎么樣的呢!
當直接獲取配置參數時,走的代碼流程就只有這倆個。
第一段是給加上前綴app
第二段是循環在config文件中獲取數據。
這段代碼如果你直接斷點調試的話是看不到什么效果的,咔咔把這段代碼給大家移到外面去執行,就會看的很清楚了。

咔咔將這段代碼給移到了index控制器中,這樣就可以看到的很清晰了
先看打印結果,確認沒啥問題
其實這里的代碼如果放在源碼中執行你會看到很多其它的信息,會很影響信息的解讀的。
但是咱們移植出來后,就可以確保代碼的運行時沒有其它的雜亂信息,有利於對信息的正確解讀。
然后緊接着看這段代碼,這段代碼之前咔咔看的時候感覺沒什么,但是越看你會越發現這塊代碼的設計很是優秀。
為什么會這樣說呢!
首先這段代碼會走第一次循環就是執行app,這次執行會在全部的config中獲取出鍵值為app的配置信息。
然后把值再次賦值給config變量,執行第二次循環為app_name。
這里循環獲取數據就是在第一次循環獲取數據的基礎上得到的。也就是第二次是在$config['app']下獲取的數據。
由此可見這段代碼設計的是多好啊!
至於其它倆個方法就交給你們了,可以簡單的試着跟着咔咔一樣把代碼移植出來,然后一步一步的解讀。
你就會發現代碼的優美之處,看的多了,對於以后自己寫代碼也會提供很多的思路的。
十二、總結
對於框架中config源碼的解析就到這里結束了,其實源碼的解析並不是很多,而是用了大量的篇幅來介紹了間接使用的一些技術。
雖說這些技術在這個已經成型的框架中不能再進行好好的利用,但是最起碼讓我們知道了他們每一個擴展的作用。
例如Yaconf對於項目配置這塊會有很大的幫助,可以讓配置文件跟項目分離,確保項目安全和跟運維之間的協同。
在例如開篇說的ArrayAccess,這個就是提供像訪問數組一樣訪問對象的接口而已,這個也就是一種好的思想,同理在以后得開發中也可以借鑒這種思想。
在配置文件這一篇中,咔咔認為最重要的就是使用工廠模式加載的不同類型配置文件,在這一節中咔咔也說了后期會在出一篇文章在進行解析的,這一節點的文章如果沒事的話真的可以好好的閱讀一下。
這個也是目前在閱讀源碼的過程中直接碰到的第一個設計模式,后邊會遇到越來越多的設計模式,遇到在進行解析
之前跟着咔咔一起實現的優化框架源碼的過程中,這個配置一定要改過來,否則你需要把框架所有的配置類型都需要改為對應的。

❝堅持學習、堅持寫博、堅持分享是咔咔從業以來一直所秉持的信念。希望在偌大互聯網中咔咔的文章能帶給你一絲絲幫助。我是咔咔,下期見。
❞