1. 程式人生 > >Laravel驅動管理類Manager的分析和使用

Laravel驅動管理類Manager的分析和使用

### Laravel驅動管理類Manager的分析和使用 > 第一部分 概念說明 > > 第二部分 Illuminate\Support\Manager原始碼 > > 第三部分 Manager類的使用 ##### 第一部分:概念解釋 > 結合實際解釋一下,啥是驅動:當我點了份外賣,那麼外賣小哥無論如何都要講外賣送到我的手中,我不會關心小哥走的是絲綢之路,還是強者之路,更不會關心他是騎著飛機、坦克還是大炮送來的。我只要我的外賣到我的手中。 > > 歸納一下,我點外賣要就要得到外賣,這就是契約,這就是介面規定的功能。 > > 小哥走什麼路線,什麼交通工具是他自己的實現,也就是各種驅動。 > > 是不是和laravel的契約和服務提供者概念很相似呢? > > 只不過今天要講解的Manager更加強調管理各個驅動,提前將所有的驅動全部註冊好,在使用的時候直接解析或者切換。 > > 說到這道友們應該理解了,Manager能做的事情Container和Provider能夠做的更好。 > > 那麼Manager和Container、Provider的區別在哪呢?這裡我只說明我的理解,Container和Provider更加的偏向框架層級,雖然也可以非侵入式的擴充套件和修改,但是相對Manager稍加麻煩,我將Manager看做一個小型的Container,裡面包含了我要實現某個功能的各種驅動(實現),Manager更加的偏向業務邏輯層。 > > 當要頻繁切換一個功能實現的時候(他更像一個頻繁更換內容,但是說明書不換的元件,俗話說的換湯不換藥),我可能會選擇Manager(比如傳送簡訊,可以使用阿里大於,京東永珍,飛鴿等等),因為他更加輕量。當要實現一個系統級的服務的時候,我會選擇Container和Provider,比如上一篇中的日誌服務。 ##### 第二部分:原始碼說明 ```php # 直接上程式碼 挺簡單的一個類,基本可以見名知意。未展示屬性 app = $container; $this->container = $container; $this->config = $container->make('config'); } // 此類是一個抽象類 這個方法用來返回預設的驅動名 abstract public function getDefaultDriver(); public function driver($driver = null) { $driver = $driver ?: $this->getDefaultDriver(); if (is_null($driver)) { throw new InvalidArgumentException(sprintf( // 此處的static顯然是實際呼叫該方法的類 'Unable to resolve NULL driver for [%s].', static::class )); } // 有點類似單例的寫法 // 如果要解析的驅動已經解析過 那麼直接返回 // 如果沒有解析過 那麼解析 並掛載到類中 if (! isset($this->drivers[$driver])) { $this->drivers[$driver] = $this->createDriver($driver); } return $this->drivers[$driver]; } // 建立指定驅動 // 要注意此類中傳遞的$driver就是指定驅動的名字 protected function createDriver($driver) { // First, we will determine if a custom driver creator exists for the given driver and // if it does not we will check for a creator method for the driver. Custom creator // callbacks allow developers to build their own "drivers" easily using Closures. # 官方註釋已經非常清晰了 # 如果要解析的驅動,是由我們手動通過鍵值對註冊進來的 那麼就呼叫對應的閉包 # 否則觸發魔術方法__call # 顯然Manager本類中並不存在額外的方法,所以魔術方法呼叫的方法,也要我們在子類中實現 # 以上就是兩種從Manager中返回驅動的方式了 if (isset($this->customCreators[$driver])) { return $this->callCustomCreator($driver); } else { $method = 'create'.Str::studly($driver).'Driver'; if (method_exists($this, $method)) { return $this->$method(); } } throw new InvalidArgumentException("Driver [$driver] not supported."); } // 上面說的通過此方法呼叫我們註冊進來的閉包 從而返回驅動 protected function callCustomCreator($driver) { return $this->customCreators[$driver]($this->container); } // 這個就是註冊閉包進來 // 你當然可以在業務邏輯中、甚至是指定的中介軟體中擴充套件你的Manager類 // 但我更喜歡在ServiceProvider的boot方法中進行擴充套件 public function extend($driver, Closure $callback) { $this->customCreators[$driver] = $callback; return $this; } public function getDrivers() { return $this->drivers; } // __call魔術方法 從Manager中解析驅動的第二種方式 public function __call($method, $parameters) { return $this->driver()->$method(...$parameters); } ``` ##### 第三部分:使用(依然通過日誌這個不恰當例子進行展示) ```php 1 建立契約 app拿到容器例項,也就意味著你可以做很多事情 public function getDefaultDriver() { // 你也可以將返回的字串寫在配置中 等等 return 'elasticsearch'; } // 展示魔術方法解析驅動 // $logManager->driver('elasticsearch')時觸發 public function createElasticsearchDriver() { // 上一篇有簡單示例 return '你的es日誌驅動'; } // 檢視manager中的驅動 public function getCustomCreators() { return $this->customCreators; } } 3 建立不同的日誌驅動 pushHandler($rotatingHandler); // 隨便加點什麼吧 $procesccor1 = new MemoryPeakUsageProcessor(); $procesccor2 = new MemoryUsageProcessor(); $logger->pushProcessor($procesccor1); $logger->pushProcessor($procesccor2); $this->logger = $logger; } public function logCertains($level, $foo) { $this->logger->{$level}($foo); } } pushHandler($streamHandler); $this->logger = $logger; } public function logCertains($level, $foo) { $this->logger->{$level}($foo); } } 4 建立服務提供者 # 此處說明一下 為什麼使用singleton進行繫結,因為我在boot方法中兩次解析manager為了將其擴充套件,保證每次解析都是同一個manager, # 也就修改了繫結到容器的manager,一旦在register方法中使用bind繫結的話,每次從容器中解析出來的都會是一個全新的manager, # 也就是說我們的boot方法白白浪費了,也就自然不能夠進行任何的操作了。其實laravel為了解決這個問題還有其他方法, # 請各位仔細檢視服務提供者部分的文件,我這裡選擇在boot方法中對manager進行擴充套件,其實你可以在任何你喜歡的地方擴充套件。 php artisan make:provider LogManagerServiceProvider app->singleton('logManager', function ($app) { return new LogManager($app); }); } /** * Bootstrap services. * * @return void */ public function boot() { // 擴充套件我們的logmanager $this->app['logManager']->extend('stream', function () { return new StreamDriver(); }); $this->app['logManager']->extend('rotate', function () { return new RotateDriver(); }); // dd($this->app['logManager']->getCustomCreators()); } } 5 註冊服務 config/app.php ... App\Providers\RouteServiceProvider::class, // 註冊自定義日誌服務 App\Providers\LogServiceProvider::class, // 註冊日誌管理服務 App\Providers\LogManagerServiceProvider::class, 6 使用測試 Route::get('logmanager', function () { resolve('logManager')->driver('stream')->logCertains('emergency', 'something emergency'); resolve('logManager')->driver('rotate')->logCertains('debug', 'debug something'); }); ``` 以上程式碼比較簡單,各位領會精神就好,大家可以結合前面說過的facade,仿照laravel原生Log服務實現各功能一致的log manager。 今天沒有下集預告,發現錯誤歡迎指正,感謝