擴(kuò)展框架

2018-12-17 10:49 更新

管理者和工廠

Laravel 有幾個(gè) Manager 類,用來管理創(chuàng)建基于驅(qū)動(dòng)的組件。這些類包括緩存、session 、認(rèn)證和隊(duì)列組件。管理者類負(fù)責(zé)基于應(yīng)用程序的配置建立一個(gè)特定的驅(qū)動(dòng)實(shí)現(xiàn)。例如,CacheManager 類可以建立 APC 、 Memcached 、文件和各種其他的緩存驅(qū)動(dòng)實(shí)現(xiàn)。

這些管理者都擁有 extend 方法,可以簡(jiǎn)單地用它來注入新的驅(qū)動(dòng)解析功能到管理者。我們將會(huì)在下面的例子,隨著講解如何為它們注入自定義驅(qū)動(dòng)支持,涵蓋這些管理者的內(nèi)容。

注意: 建議花點(diǎn)時(shí)間來探索 Laravel 附帶的各種 Manager 類,例如:CacheManagerSessionManager??催^這些類將會(huì)讓你更徹底了解 Laravel 表面下是如何運(yùn)作。所有的管理者類繼承 Illuminate\Support\Manager 基礎(chǔ)類,它提供一些有用、常見的功能給每一個(gè)管理者。

           

緩存

為了擴(kuò)展 Laravel 緩存功能,我們將會(huì)使用 CacheManagerextend 方法,這方法可以用來綁定一個(gè)自定義驅(qū)動(dòng)解析器到管理者,并且是全部的管理者類通用的。例如,注冊(cè)一個(gè)新的緩存驅(qū)動(dòng)名為「mongo」,我們將執(zhí)行以下操作:

Cache::extend('mongo', function($app){
    return Cache::repository(new MongoStore);});

           

傳遞到 extend 方法的第一個(gè)參數(shù)是驅(qū)動(dòng)的名稱。這將會(huì)對(duì)應(yīng)到你的 config/cache.php 配置文件里的 driver 選項(xiàng)。第二個(gè)參數(shù)是個(gè)應(yīng)該返回 Illuminate\Cache\Repository 實(shí)例的閉包。 $app 將會(huì)被傳遞到閉包,它是 Illuminate\Foundation\Application                和服務(wù)容器的實(shí)例。

Cache::extend 的調(diào)用可以在新的 Laravel 應(yīng)用程序默認(rèn)附帶的 App\Providers\AppServiceProviderboot 方法中完成,或者你可以建立自己的服務(wù)提供者來放置這個(gè)擴(kuò)展 - 記得不要忘記在 config/app.php 的提供者數(shù)組注冊(cè)提供者。

要建立自定義緩存驅(qū)動(dòng),首先需要實(shí)現(xiàn) Illuminate\Contracts\Cache\Store contract 。所以,我們的 MongoDB 緩存實(shí)現(xiàn)將會(huì)看起來像這樣:

class MongoStore implements Illuminate\Contracts\Cache\Store {

    public function get($key) {}
    public function put($key, $value, $minutes) {}
    public function increment($key, $value = 1) {}
    public function decrement($key, $value = 1) {}
    public function forever($key, $value) {}
    public function forget($key) {}
    public function flush() {}}

           

我們只需要使用 MongoDB 連接來實(shí)現(xiàn)這些方法。當(dāng)實(shí)現(xiàn)完成,就可以完成自定義驅(qū)動(dòng)注冊(cè):

Cache::extend('mongo', function($app){
    return Cache::repository(new MongoStore);});

           

如果你正在考慮要把自定義緩存驅(qū)動(dòng)代碼放在哪里,請(qǐng)考慮把它放上 Packagist !或者,你可以在 app 的目錄中建立 Extensions 命名空間。記得 Laravel 沒有嚴(yán)格的應(yīng)用程序架構(gòu),你可以依照喜好自由的組織應(yīng)用程序。

           

Session

自定義 session 驅(qū)動(dòng)來擴(kuò)展 Laravel 和擴(kuò)展緩存系統(tǒng)一樣簡(jiǎn)單。我們將會(huì)再一次使用 extend 方法來注冊(cè)自定義代碼:

Session::extend('mongo', function($app){
    // Return implementation of SessionHandlerInterface});

           

在哪里擴(kuò)展 Session

你應(yīng)該把 session 擴(kuò)展代碼放置在 AppServiceProviderboot 方法里。

實(shí)現(xiàn) Session 擴(kuò)展

要注意我們的自定義緩存驅(qū)動(dòng)應(yīng)該要實(shí)現(xiàn) SessionHandlerInterface 。這個(gè)接口只包含少數(shù)需要實(shí)現(xiàn)的簡(jiǎn)單方法。一個(gè)基本的 MongoDB 實(shí)現(xiàn)會(huì)看起來像這樣:

class MongoHandler implements SessionHandlerInterface {

    public function open($savePath, $sessionName) {}
    public function close() {}
    public function read($sessionId) {}
    public function write($sessionId, $data) {}
    public function destroy($sessionId) {}
    public function gc($lifetime) {}}

           

因?yàn)檫@些方法不像緩存的 StoreInterface 一樣容易理解,讓我們快速地看過這些方法做些什么:

  • open 方法通常會(huì)被用在基于文件的 session 保存系統(tǒng)。因?yàn)?Laravel 附帶一個(gè) file session 驅(qū)動(dòng),幾乎不需要在這個(gè)方法放任何東西。你可以讓它留空。PHP 要求我們?nèi)?shí)現(xiàn)這個(gè)方法,事實(shí)上明顯是個(gè)差勁的接口設(shè)計(jì) (我們將會(huì)晚點(diǎn)討論它)。

  • close 方法,就像 open 方法,通常也可以忽略。對(duì)大部份的驅(qū)動(dòng)來說,并不需要它。

  • read 方法應(yīng)該返回與給定 $sessionId 關(guān)聯(lián)的 session 數(shù)據(jù)的字串形態(tài)。當(dāng)你的驅(qū)動(dòng)取回或保存 session 數(shù)據(jù)時(shí)不需要做任何序列化或進(jìn)行其他編碼,因?yàn)?Laravel 將會(huì)為你進(jìn)行序列化

  • write 方法應(yīng)該寫入給定 $data 字串與 $sessionId 的關(guān)聯(lián)到一些永久存儲(chǔ)系統(tǒng),例如:MongoDB、 Dynamo、等等。

  • destroy 方法應(yīng)該從永久存儲(chǔ)移除與 $sessionId 關(guān)聯(lián)的數(shù)據(jù)。

  • gc 方法應(yīng)該銷毀所有比給定 $lifetime UNIX 時(shí)間戳記還舊的 session 數(shù)據(jù)。對(duì)于會(huì)自己過期的系統(tǒng)如 Memcached 和 Redis,這個(gè)方法可以留空。

當(dāng) SessionHandlerInterface 實(shí)現(xiàn)完成,我們準(zhǔn)備好要用 Session 管理者注冊(cè)它:

Session::extend('mongo', function($app){
    return new MongoHandler;});

           

當(dāng) session 驅(qū)動(dòng)已經(jīng)被注冊(cè),我們可以在 config/session.php 配置文件使用 mongo 驅(qū)動(dòng)。

注意: 記住,如果你寫了個(gè)自定義 session 處理器,請(qǐng)?jiān)?Packagist 分享它!

           

認(rèn)證

認(rèn)證可以用與緩存和 session 功能相同的方法擴(kuò)展。再一次的,使用我們已經(jīng)熟悉的 extend 方法:

Auth::extend('riak', function($app){
    // 返回 Illuminate\Contracts\Auth\UserProvider 的實(shí)現(xiàn)});

           

UserProvider 實(shí)現(xiàn)只負(fù)責(zé)從永久存儲(chǔ)系統(tǒng)抓取 Illuminate\Contracts\Auth\Authenticatable 實(shí)現(xiàn),存儲(chǔ)系統(tǒng)例如: MySQL 、 Riak ,等等。這兩個(gè)接口讓 Laravel 認(rèn)證機(jī)制無論用戶數(shù)據(jù)如何保存或用什么種類的類來代表它都能繼續(xù)運(yùn)作。

讓我們來看一下 UserProvider contract :

interface UserProvider {

    public function retrieveById($identifier);
    public function retrieveByToken($identifier, $token);
    public function updateRememberToken(Authenticatable $user, $token);
    public function retrieveByCredentials(array $credentials);
    public function validateCredentials(Authenticatable $user, array $credentials);}

           

retrieveById 函數(shù)通常接收一個(gè)代表用戶的數(shù)字鍵,例如:MySQL 數(shù)據(jù)庫(kù)的自動(dòng)遞增 ID。這方法應(yīng)該取得符合 ID 的 Authenticatable 實(shí)現(xiàn)并返回。

retrieveByToken 函數(shù)用用戶唯一的 $identifier 和保存在 remember_token 字段的「記住我」 $token 來取得用戶。跟前面的方法一樣,應(yīng)該返回 Authenticatable 的實(shí)現(xiàn)。

updateRememberToken 方法用新的 $token 更新 $userremember_token 字段。新 token 可以是在「記住我」成功地登錄時(shí),傳入一個(gè)新的 token,或當(dāng)用戶注銷時(shí)傳入一個(gè) null。

retrieveByCredentials 方法接收當(dāng)嘗試登錄應(yīng)用程序時(shí),傳遞到 Auth::attempt 方法的憑證數(shù)組。這個(gè)方法應(yīng)該接著「查找」底層使用的永久存儲(chǔ),找到符合憑證的用戶。這個(gè)方法通常會(huì)對(duì) $credentials['username'] 用「 where 」條件查找。 并且應(yīng)該返回一個(gè) UserInterface 接口的實(shí)現(xiàn)。這個(gè)方法不應(yīng)該嘗試做任何密碼驗(yàn)證或認(rèn)證。            

validateCredentials 方法應(yīng)該通過比較給定的 $user$credentials 來驗(yàn)證用戶。舉例來說,這個(gè)方法可以比較 $user->getAuthPassword() 字串跟 Hash::make 后的 $credentials['password']。這個(gè)方法應(yīng)該只驗(yàn)證用戶的憑證數(shù)組并且返回布爾值。

現(xiàn)在我們已經(jīng)看過 UserProvider 的每個(gè)方法,接著來看一下 Authenticatable。記住,提供者應(yīng)該從 retrieveByIdretrieveByCredentials 方法返回這個(gè)接口的實(shí)現(xiàn):

interface Authenticatable {

    public function getAuthIdentifier();
    public function getAuthPassword();
    public function getRememberToken();
    public function setRememberToken($value);
    public function getRememberTokenName();}

           

這個(gè)接口很簡(jiǎn)單。 The getAuthIdentifier 方法應(yīng)該返回用戶的「主鍵」。在 MySQL 后臺(tái),同樣,這將會(huì)是個(gè)自動(dòng)遞增的主鍵。getAuthPassword 應(yīng)該返回用戶哈希過的密碼。這個(gè)接口讓認(rèn)證系統(tǒng)可以與任何用戶類一起運(yùn)作,無論你使用什么 ORM 或保存抽象層。默認(rèn),Laravel 包含一個(gè)實(shí)現(xiàn)這個(gè)接口的 User 類在 app 文件夾里,所以你可以參考這個(gè)類當(dāng)作實(shí)現(xiàn)的例子。

最后,當(dāng)我們已經(jīng)實(shí)現(xiàn)了 UserProvider,我們準(zhǔn)備好用 Auth facade 來注冊(cè)擴(kuò)展:

Auth::extend('riak', function($app){
    return new RiakUserProvider($app['riak.connection']);});

           

extend 方法注冊(cè)驅(qū)動(dòng)之后,在你的 config/auth.php 配置文件切換到新驅(qū)動(dòng)。

           

基于服務(wù)容器的擴(kuò)展

幾乎每個(gè) Laravel 框架引入的服務(wù)提供者都會(huì)綁定對(duì)象到服務(wù)容器中。你可以在 config/app.php 配置文件中找到應(yīng)用程序的服務(wù)提供者清單。如果你有時(shí)間,你應(yīng)該瀏覽過這里面每一個(gè)提供者的源代碼。通過這樣做,你將會(huì)更了解每一個(gè)提供者添加什么到框架,以及用什么鍵值來綁定各種服務(wù)到服務(wù)容器。

例如, HashServiceProvider 綁定 hash 做為鍵值到服務(wù)容器,它將解析成 Illuminate\Hashing\BcryptHasher 實(shí)例。你可以在應(yīng)用程序中覆寫這個(gè) IoC 綁定,輕松地?cái)U(kuò)展并覆寫這個(gè)類。例如:

<?php namespace App\Providers;class SnappyHashProvider extends \Illuminate\Hashing\HashServiceProvider {

    public function boot()
    {
        parent::boot();

        $this->app->bindShared('hash', function()
        {
            return new \Snappy\Hashing\ScryptHasher;
        });
    }}

           

要注意的是這個(gè)類擴(kuò)展 HashServiceProvider,不是默認(rèn)的 ServiceProvider 基礎(chǔ)類。當(dāng)你擴(kuò)展了服務(wù)提供者,在 config/app.php 配置文件把 HashServiceProvider 換成你擴(kuò)展的提供者名稱。

這是被綁定在容器的所有核心類的一般擴(kuò)展方法。實(shí)際上,每個(gè)以這種方式綁定在容器的核心類都可以被覆寫。再次強(qiáng)調(diào),看過每個(gè)框架引入的服務(wù)提供者將會(huì)使你熟悉:每個(gè)類被綁在容器的哪里、它們是用什么鍵值綁定。這是個(gè)好方法可以了解更多關(guān)于 Laravel 如何結(jié)合它們。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)