❝在上期聊了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,這個就是提供像訪問陣列一樣訪問物件的介面而已,這個也就是一種好的思想,同理在以後得開發中也可以借鑑這種思想。
在配置檔案這一篇中,咔咔認為最重要的就是使用工廠模式載入的不同型別配置檔案,在這一節中咔咔也說了後期會在出一篇文章在進行解析的,這一節點的文章如果沒事的話真的可以好好的閱讀一下。
這個也是目前在閱讀原始碼的過程中直接碰到的第一個設計模式,後邊會遇到越來越多的設計模式,遇到在進行解析
之前跟著咔咔一起實現的優化框架原始碼的過程中,這個配置一定要改過來,否則你需要把框架所有的配置型別都需要改為對應的。
❝堅持學習、堅持寫博、堅持分享是咔咔從業以來一直所秉持的信念。希望在偌大網際網路中咔咔的文章能帶給你一絲絲幫助。我是咔咔,下期見。
❞