擴(kuò)展框架

2018-12-17 10:49 更新

管理者和工廠

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

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

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

           

緩存

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

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

           

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

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

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

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 連接來實現(xiàn)這些方法。當(dāng)實現(xiàn)完成,就可以完成自定義驅(qū)動注冊:

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

           

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

           

Session

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

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

           

在哪里擴(kuò)展 Session

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

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

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

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) {}}

           

因為這些方法不像緩存的 StoreInterface 一樣容易理解,讓我們快速地看過這些方法做些什么:

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

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

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

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

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

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

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

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

           

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

注意: 記住,如果你寫了個自定義 session 處理器,請在 Packagist 分享它!

           

認(rèn)證

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

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

           

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

讓我們來看一下 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ù)通常接收一個代表用戶的數(shù)字鍵,例如:MySQL 數(shù)據(jù)庫的自動遞增 ID。這方法應(yīng)該取得符合 ID 的 Authenticatable 實現(xiàn)并返回。

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

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

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

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

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

interface Authenticatable {

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

           

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

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

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

           

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

           

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

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

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

<?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;
        });
    }}

           

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

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


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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號