ThinkPHP門面源碼解析

本文主要描述了門面的使用和實現過程,并且對源碼進行剖析。

前言

使用框架的伙伴應該都知道在5.1時框架新增了一個特性那就是本文將編寫的門面,也就是facade這個特性。

使用過這個特性的都明白其中的好處,那就是方法調用可以直接靜態進行調用,不用再使用關鍵字static來定義。

接下來咔咔將會從以下幾個方面帶著大家探索屬于門面的故事。

一、簡單認識一下在框架中的門面的好處

在之前有寫過配置文件加載一文,在那一文中的最后提到過配置信息獲取的幾種方式。

其中有一種方式就是Config::get(),到這篇文章應該都知道使用Config獲取配置信息時,必須先得引入use thinkfacadeConfig,又因為在系統中注冊了別名,所以直接使用use Config即可。

雖說我們使用的是use thinkfacadeConfig,但是實際調用的方法卻是thinkphp/library/think/Facade.php中的__callStatic方法。

然后會執行同文件的createFacade方法。

雖說現在還沒有看源碼,看著知道就好了,在調用createFacade方法時是直接從容器類里邊獲取的。

在學習容器時我們都知道容器是使用了注冊樹模式,需要使用對應對象實例的時候就可以直接獲取,這樣就避免了一個類反復的創建。這就是其中的一個優點。利用容器的特性

對于以前使用config來說,需要使用config的命名空間,然后進行實例化才能進行調用。

如果此時config不讓使用了,需要使用自己創建的config類,如果沒有使用門面模式,就需要修改大量的代碼,而且是全局的。

但是如果使用了框架中的facade門面模式之后,你就只需要重寫getFacadeClass這個方法即可,只需要改變里邊的返回結果誒自己定義的即可,因為對于其它文件調用的地方它們不關心實例調用的是什么,只關心方法名和返回結果。

二、學習框架中facade的使用

首先創建一個控制器Facade,并且寫上以下內容。

這里只是簡單的使用門面的方式來獲取配置文件信息。

ThinkPHP門面源碼解析在這里可以看到使用的是use Config,這個就是config類的別名。

別名的設置是在base.php中設置的。

ThinkPHP門面源碼解析在框架中如何正確的使用facade呢!

在app目錄下新建一個文件夾facade,用來專門存放門面類。

這里創建了一個Sessions的類。

ThinkPHP門面源碼解析先做一個測試,檢測代碼是否寫的有問題。在控制器的facade文件中進行測試。

這就是沒有使用門面時的處理方式,需要引入對應的類,然后進行實例化,在用實例化的類進行方法調用。

ThinkPHP門面源碼解析打印結果,結果就是我們預期的結果。

ThinkPHP門面源碼解析那么這份代碼怎么改為門面模式呢!跟著咔咔的腳步一步一步的來。

先在kaka目錄下建立倆個目錄,分別為facade和util

ThinkPHP門面源碼解析為什么要建立這倆個文件夾呢!util大家應該都知道那就是工具類,這種類文件是可以在其它項目中公用的。

也就是說我們只需要實現一份然后在其它項目中用的時候直接拿過去就可以了。

所以就可以直接把文件復制到util目錄下,記得修改命名空間即可。

ThinkPHP門面源碼解析然后在到facade目錄下新建一個Sessions的類,并且繼承Facade。然后寫上一下內容。

ThinkPHP門面源碼解析此時我們在來到控制器進行測試一下。

會發現結果跟之前是一致的,但是很明顯的一個區別就是使用了facade模式后,可以直接使用靜態方式進行調用。

還記得在之前說過門面的一個好處嗎?

假設這個Sessions工具類在未來的一天停止使用了,那么我們只需要修改getFacadeClass方法里邊的內容即可。

ThinkPHP門面源碼解析ThinkPHP門面源碼解析

三、優化在框架中facade的使用

在上文中我們從實例化類到使用門面方式實現了同一個功能。

雖說想要實現的效果顯示出來了,但是代碼還是不夠簡潔優美的,結構也比較混亂。

接下來咔咔會給大家提供一個可行的方案,如果你有其它的方案可以提出來哈!評論區見。

在正常開發工作中自定義的門面類不可能只有一個或者幾個,在復雜的項目中門面類會有很多。

既然多,那就需要進行管理。

ThinkPHP門面源碼解析首先建立一個屬于門面的配置類。

并將代理類和實際類對應起來,然后設置別名。

ThinkPHP門面源碼解析此時需要建立一個鉤子文件,將門面類注冊和門面類別名注冊都放在里邊進行執行。

ThinkPHP門面源碼解析還有最后一步,那就是鉤子文件雖然創建了但是沒有執行。

那么鉤子文件應該什么時候執行呢!那就是在應用初始化的時候進行加載。

在TP5.1中應用初始化的配置是在application/tags.php這個文件中。

在應用初始化的配置項里把鉤子文件配置進去即可。

ThinkPHP門面源碼解析測試

最后一步就是測試了,依然是執行application/index/controller/Facade.php文件中的getUserInfo方法。

根據測試結果可以得知我們的方案代碼編寫沒有問題。

ThinkPHP門面源碼解析ThinkPHP門面源碼解析這里有沒有發現一個問題,就是既然在鉤子中定義了門面類的別名了,但是在這里并沒有使用。

接下來我們使用別名來測試一下。

ThinkPHP門面源碼解析ThinkPHP門面源碼解析

四、門面類源碼解析

在解析源碼之前先認識倆個方法。

  • __callStatic:當訪問不存在的靜態方法時,會調用此方法。
  • call_user_func_array:可以直接用此函數來直接調用函數。

我們就從獲取配置文件開始解析

ThinkPHP門面源碼解析執行Config::get(‘facade.’);會執行到文件thinkphp/library/think/facade/Config.php中。

在這個文件中就是之前說的,如果存在getFacadeClass方法就會直接返回對應的別名。

如果不存在的話就需要使用bind方法來進行門面綁定。

這里如果不明白就需要去文檔好好看看門面那一章節哈!

ThinkPHP門面源碼解析在上邊類中是不存在get方法的,所以就會直接調用thinkphp/library/think/Facade.php文件中的__callStatic方法。

這個方法就是文章開頭就直接說明的,訪問不存在的靜態的方法時則會調用此方法。

ThinkPHP門面源碼解析接著就會執行本類中的createFacade這個方法

在這個方法里邊有一行代碼是這個樣子的$facadeClass = static::getFacadeClass();這段代碼會在下文做詳細的說明。

因為在子類中也有同樣的方法,在本類中也有同樣的方法但是在本類中的方法是沒有任何返回值的。

這時你有沒有一絲絲的困惑,這里使用的static到底會執行哪里的方法?;蛘哌@樣想,為什么會執行子類的方法。

保留這些疑問將會在下文給你細細的講來,先來把門面類的源碼看完。

在這個方法中主要看我圈起來的幾個地方。

第一處就是從子類的getFacadeClass方法獲取類的別名。

第二處是當子類沒有getFacadeClass方法時,從手動綁定的屬性中獲取。

第三處就是之前文章提到的容器了,這里就不對這里做詳細說明了,如果不會的點開主頁去看之前的文章。

ThinkPHP門面源碼解析
createFacade方法

五、static關鍵字

在這里不得不說明一下static這個關鍵字。

新學習的伙伴估計只能知道static是用來定義靜態變量和靜態方法的。

當然這里不會去給大家說怎么定義靜態方法和靜態變量,而是說一個非常非常小的細節點。

先看一個實例,這個實例也是在閱讀門面源碼時,咔咔根據門面源碼改編過來的。

咔咔這里新建了倆個文件,分別為test和test1。

test繼承test1文件,并且都有同樣的方法getKaka。

ThinkPHP門面源碼解析
新建倆個文件

test的源碼

ThinkPHP門面源碼解析
test源碼

test1源碼

ThinkPHP門面源碼解析控制器進行調用

ThinkPHP門面源碼解析打印結果ThinkPHP門面源碼解析這個時候有沒有一點點疑惑,這里怎么打印出來的是147,而不是456呢!

修改test1的代碼,把static改為self

ThinkPHP門面源碼解析打印結果

ThinkPHP門面源碼解析使用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文件中這個行代碼。

因為這行代碼調用的方法在子類和父類都存在,所以咔咔為了不讓大家出現迷惑就寫出來做一個簡單的介紹。

ThinkPHP門面源碼解析
thinkphp/library/think/Facade.php

六、總結

先來一份門面流程圖,可以更清晰的看到門面類的具體執行流程。

ThinkPHP門面源碼解析門面類的源碼很是簡單,除了幾個不太常見的知識點,代碼相信都看的明白。

這里主要是對閱讀完門面類后,做一個小總結。

門面類主要是結合了容器來實現的一個功能,因為需要容器來返回對應的實例,關于容器的文章也已經完成了,如果對容器有不會的可以在文章的開頭去看對應的文章即可。

本文中給大家介紹了門面在容器中如何使用,并且給大家提供了最優的使用方式,這里的最優是咔咔個人見解,因為這種方式咔咔使用了接近倆年了。

無論從代碼的健壯性還是擴展性都是非常實用的。

再就是關于static關鍵字,給大家做的一點點冷門知識得補充,當一個類繼承一個類時,在父類實用static關鍵字時,默認調用的子類的方法。

這里的總結只是針對于本文的實例。

其實在這里咔咔還想給大家說明一個點就是return call_user_func_array([static::createFacade(), $method], $params);

因為在以前的用法的哥參數就直接是方法,但是在這里碰到了數組形式,那么這個數組中的倆個值都代表的是什么呢!

第一個值為實例,第二個值為實例中的方法。

關于call_user_func_array這個方法的使用咔咔就不去做案例給大家看了,只需要知道它會去執行傳入得方法即可。

到這里關于門面的源碼解析就結束了,最重要的還是理解容器,因為門面就是在容器的基礎上實現的,這也就是咔咔先寫容器在寫門面的原因。

還有就是關于門面的使用咔咔也給出了方案,如你有更好的方案可以在評論區給一個大概的思路。

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

? 版權聲明
THE END
喜歡就支持一下吧
點贊8 分享