面試官:聊一下對框架配置載入的理解吧!
❝在上期聊了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,這個就是提供像訪問數組一樣訪問對象的介面而已,這個也就是一種好的思想,同理在以後得開發中也可以借鑒這種思想。
在配置文件這一篇中,咔咔認為最重要的就是使用工廠模式載入的不同類型配置文件,在這一節中咔咔也說了後期會在出一篇文章在進行解析的,這一節點的文章如果沒事的話真的可以好好的閱讀一下。
這個也是目前在閱讀源碼的過程中直接碰到的第一個設計模式,後邊會遇到越來越多的設計模式,遇到在進行解析
之前跟著咔咔一起實現的優化框架源碼的過程中,這個配置一定要改過來,否則你需要把框架所有的配置類型都需要改為對應的。

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