本文主要描述了門面的使用和實現過程以及源碼的深度解析。php
@TOCthinkphp
前言
使用框架的夥伴應該都知道在5.1時框架新增了一個特性那就是本文將編寫的門面,也就是facade這個特性。數組
使用過這個特性的都明白其中的好處,那就是方法調用能夠直接靜態進行調用,不用再使用關鍵字static來定義。app
接下來咔咔將會從如下幾個方面帶着你們探索屬於門面的故事。框架
1、簡單認識一下在框架中的門面的好處
在以前有寫過配置文件加載一文,在那一文中的最後提到過配置信息獲取的幾種方式。函數
其中有一種方式就是Config::get(),到這篇文章應該都知道使用Config獲取配置信息時,必須先得引入use think\facade\Config
,又由於在系統中註冊了別名,因此直接使用use Config
便可。工具
雖然說咱們使用的是use think\facade\Config
,可是實際調用的方法倒是thinkphp/library/think/Facade.php
中的__callStatic
方法。學習
而後會執行同文件的createFacade
方法。測試
雖然說如今尚未看源碼,看着知道就行了,在調用createFacade
方法時是直接從容器類裏邊獲取的。優化
在學習容器時咱們都知道容器是使用了註冊樹模式,須要使用對應對象實例的時候就能夠直接獲取,這樣就避免了一個類反覆的建立。這就是其中的一個優勢。利用容器的特性
對於之前使用config來講,須要使用config的命名空間,而後進行實例化才能進行調用。
若是此時config不讓使用了,須要使用本身建立的config類,若是沒有使用門面模式,就須要修改大量的代碼,並且是全局的。
可是若是使用了框架中的facade門面模式以後,你就只須要重寫getFacadeClass
這個方法便可,只須要改變裏邊的返回結果誒本身定義的便可,由於對於其它文件調用的地方它們不關心實例調用的是什麼,只關心方法名和返回結果。
2、學習框架中facade的使用
首先建立一個控制器Facade,而且寫上如下內容。
這裏只是簡單的使用門面的方式來獲取配置文件信息。
在這裏能夠看到使用的是use Config
,這個就是config類的別名。
別名的設置是在base.php
中設置的。
在框架中如何正確的使用facade呢!
在app目錄下新建一個文件夾facade,用來專門存放門面類。
這裏建立了一個Sessions的類。
先作一個測試,檢測代碼是否寫的有問題。在控制器的facade文件中進行測試。
這就是沒有使用門面時的處理方式,須要引入對應的類,而後進行實例化,在用實例化的類進行方法調用。
打印結果,結果就是咱們預期的結果。
那麼這份代碼怎麼改成門面模式呢!跟着咔咔的腳步一步一步的來。
先在kaka目錄下創建倆個目錄,分別爲facade和util
爲何要創建這倆個文件夾呢!util你們應該都知道那就是工具類,這種類文件是能夠在其它項目中公用的。
也就是說咱們只須要實現一份而後在其它項目中用的時候直接拿過去就能夠了。
因此就能夠直接把文件複製到util目錄下,記得修改命名空間便可。
而後在到facade目錄下新建一個Sessions的類,而且繼承Facade。而後寫上一下內容。
此時咱們在來到控制器進行測試一下。
會發現結果跟以前是一致的,可是很明顯的一個區別就是使用了facade模式後,能夠直接使用靜態方式進行調用。
還記得在以前說過門面的一個好處嗎?
假設這個Sessions工具類在將來的一天中止使用了,那麼咱們只須要修改getFacadeClass
方法裏邊的內容便可。
3、優化在框架中facade的使用
在上文中咱們從實例化類到使用門面方式實現了同一個功能。
雖然說想要實現的效果顯示出來了,可是代碼仍是不夠簡潔優美的,結構也比較混亂。
接下來咔咔會給你們提供一個可行的方案,若是你有其它的方案能夠提出來哈!評論區見。
在正常開發工做中自定義的門面類不可能只有一個或者幾個,在複雜的項目中門面類會有不少。
既然多,那就須要進行管理。
首先創建一個屬於門面的配置類。
並將代理類和實際類對應起來,而後設置別名。
此時須要創建一個鉤子文件,將門面類註冊和門面類別名註冊都放在裏邊進行執行。
還有最後一步,那就是鉤子文件雖然建立了可是沒有執行。
那麼鉤子文件應該何時執行呢!那就是在應用初始化的時候進行加載。
在TP5.1中應用初始化的配置是在application/tags.php
這個文件中。
在應用初始化的配置項裏把鉤子文件配置進去便可。
測試
最後一步就是測試了,依然是執行application/index/controller/Facade.php
文件中的getUserInfo
方法。
根據測試結果能夠得知咱們的方案代碼編寫沒有問題。
這裏有沒有發現一個問題,就是既然在鉤子中定義了門面類的別名了,可是在這裏並無使用。
接下來咱們使用別名來測試一下。
4、門面類源碼解析
在解析源碼以前先認識倆個方法。
__callStatic
:當訪問不存在的靜態方法時,會調用此方法。call_user_func_array
:能夠直接用此函數來直接調用函數。
咱們就從獲取配置文件開始解析
執行Config::get('facade.');
會執行到文件thinkphp/library/think/facade/Config.php
中。
在這個文件中就是以前說的,若是存在getFacadeClass
方法就會直接返回對應的別名。
若是不存在的話就須要使用bind方法來進行門面綁定。
這裏若是不明白就須要去文檔好好看看門面那一章節哈!
在上邊類中是不存在get方法的,因此就會直接調用thinkphp/library/think/Facade.php
文件中的__callStatic
方法。
這個方法就是文章開頭就直接說明的,訪問不存在的靜態的方法時則會調用此方法。
接着就會執行本類中的createFacade
這個方法
在這個方法裏邊有一行代碼是這個樣子的$facadeClass = static::getFacadeClass();
這段代碼會在下文作詳細的說明。
由於在子類中也有一樣的方法,在本類中也有一樣的方法可是在本類中的方法是沒有任何返回值的。
這時你有沒有一絲絲的困惑,這裏使用的static到底會執行哪裏的方法。或者這樣想,爲何會執行子類的方法。
保留這些疑問將會在下文給你細細的講來,先來把門面類的源碼看完。
在這個方法中主要看我圈起來的幾個地方。
第一處就是從子類的getFacadeClass
方法獲取類的別名。
第二處是當子類沒有getFacadeClass
方法時,從手動綁定的屬性中獲取。
第三處就是以前文章提到的容器了,這裏就不對這裏作詳細說明了,若是不會的點開主頁去看以前的文章。
5、static關鍵字
在這裏不得不說明一下static這個關鍵字。
新學習的夥伴估計只能知道static是用來定義靜態變量和靜態方法的。
固然這裏不會去給你們說怎麼定義靜態方法和靜態變量,而是說一個很是很是小的細節點。
先看一個實例,這個實例也是在閱讀門面源碼時,咔咔根據門面源碼改編過來的。
咔咔這裏新建了倆個文件,分別爲test和test1。
test繼承test1文件,而且都有一樣的方法getKaka。
test的源碼
test1源碼
控制器進行調用
打印結果 這個時候有沒有一點點疑惑,這裏怎麼打印出來的是147
,而不是456
呢!
修改test1的代碼,把static改成self
打印結果
使用self的代碼相信你們都看的明白,那爲何使用static就出現了有可能不太明白的結果呢!
此時就是文檔開始起做用了,可是當你打開PHP文檔會發現,在static這一篇中並無對這類狀況做出說明。
通過咔咔屢次測試和查閱資料,最終總結結果以下。
static::$test 若是有被繼承的話 默認調用子類 ,不然調用的是自身
self::$test 若是有被繼承的話,默認調用本類
放在本實例中來講明就是,當test繼承test1時。
在test1中使用static調用方法getKaka
時,默認調用的是test類中的getKaka
,也就是子類的方法。
在test1中使用self調用方法getKaka
時,默認調用的是test1類中的getKaka
,也就是本類的方法。
這個小小的細節也是咔咔無心中發現的,若是有什麼不對的地方能夠提出來,咔咔做出修改。
由於在繼承這方面還有另一種狀況,咔咔私下會進行測試,在這裏就不說明了。
這裏對這個static作出解釋主要是爲了解釋thinkphp/library/think/Facade.php
文件中這個行代碼。
由於這行代碼調用的方法在子類和父類都存在,因此咔咔爲了避免讓你們出現迷惑就寫出來作一個簡單的介紹。
6、總結
先來一份門面流程圖,能夠更清晰的看到門面類的具體執行流程。
門面類的源碼非常簡單,除了幾個不太常見的知識點,代碼相信都看的明白。
這裏主要是對閱讀完門面類後,作一個小總結。
門面類主要是結合了容器來實現的一個功能,由於須要容器來返回對應的實例,關於容器的文章也已經完成了,若是對容器有不會的能夠在文章的開頭去看對應的文章便可。
本文中給你們介紹了門面在容器中如何使用,而且給你們提供了最優的使用方式,這裏的最優是咔咔我的看法,由於這種方式咔咔使用了接近倆年了。
不管從代碼的健壯性仍是擴展性都是很是實用的。
再就是關於static關鍵字,給你們作的一點點冷門知識得補充,當一個類繼承一個類時,在父類實用static關鍵字時,默認調用的子類的方法。
這裏的總結只是針對於本文的實例。
其實在這裏咔咔還想給你們說明一個點就是return call_user_func_array([static::createFacade(), $method], $params);
由於在之前的用法的哥參數就直接是方法,可是在這裏碰到了數組形式,那麼這個數組中的倆個值都表明的是什麼呢!
第一個值爲實例,第二個值爲實例中的方法。
關於call_user_func_array
這個方法的使用咔咔就不去作案例給你們看了,只須要知道它會去執行傳入得方法便可。
到這裏關於門面的源碼解析就結束了,最重要的仍是理解容器,由於門面就是在容器的基礎上實現的,這也就是咔咔先寫容器在寫門面的緣由。
還有就是關於門面的使用咔咔也給出了方案,如你有更好的方案能夠在評論區給一個大概的思路。
堅持學習、堅持寫博、堅持分享是咔咔從業以來一直所秉持的信念。但願在偌大互聯網中咔咔的文章能帶給你一絲絲幫助。我是咔咔,下期見。