第一部分 概念说明php
第二部分 Illuminate\Support\Manager源码laravel
第三部分 Manager类的使用闭包
结合实际解释一下,啥是驱动:当我点了份外卖,那么外卖小哥不管如何都要讲外卖送到个人手中,我不会关心小哥走的是丝绸之路,仍是强者之路,更不会关心他是骑着飞机、坦克仍是大炮送来的。我只要个人外卖到个人手中。app
概括一下,我点外卖要就要获得外卖,这就是契约,这就是接口规定的功能。框架
小哥走什么路线,什么交通工具是他本身的实现,也就是各类驱动。elasticsearch
是否是和laravel的契约和服务提供者概念很类似呢?ide
只不过今天要讲解的Manager更增强调管理各个驱动,提早将全部的驱动所有注册好,在使用的时候直接解析或者切换。工具
说到这道友们应该理解了,Manager能作的事情Container和Provider可以作的更好。测试
那么Manager和Container、Provider的区别在哪呢?这里我只说明个人理解,Container和Provider更加的偏向框架层级,虽然也能够非侵入式的扩展和修改,可是相对Manager稍加麻烦,我将Manager看作一个小型的Container,里面包含了我要实现某个功能的各类驱动(实现),Manager更加的偏向业务逻辑层。ui
当要频繁切换一个功能实现的时候(他更像一个频繁更换内容,可是说明书不换的组件,俗话说的换汤不换药),我可能会选择Manager(好比发送短信,可使用阿里大于,京东万象,飞鸽等等),由于他更加轻量。当要实现一个系统级的服务的时候,我会选择Container和Provider,好比上一篇中的日志服务。
# 直接上代码 挺简单的一个类,基本能够见名知意。未展现属性 <?php namespace Illuminate\Support; use Closure; use Illuminate\Contracts\Container\Container; use InvalidArgumentException; // 值得注意的是 Manager是一个抽象类,必定要实现了其中的抽象方法getDefaultDriver才能实例化 // 咱们观察构造方法中的参数,你会不会想到在Provider中挂载Manager是一个好方法呢? public function __construct(Container $container) { $this->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); }
1 建立契约 <?php namespace App\Contracts; interface ManageLog { public function logCertains($level, $foo); } 2 建立日志组件,使用管理器管理 <?php namespace App\Components\Log; use Illuminate\Support\Manager; class LogManager extends Manager { // 这是个类,而且你能够经过$this->app拿到容器实例,也就意味着你能够作不少事情 public function getDefaultDriver() { // 你也能够将返回的字符串写在配置中 等等 return 'elasticsearch'; } // 展现魔术方法解析驱动 // $logManager->driver('elasticsearch')时触发 public function createElasticsearchDriver() { // 上一篇有简单示例 return '你的es日志驱动'; } // 查看manager中的驱动 public function getCustomCreators() { return $this->customCreators; } } 3 建立不一样的日志驱动 <?php namespace App\Drivers\Log; use App\Contracts\ManageLog; use Monolog\Logger; use Monolog\Handler\RotatingFileHandler; use Monolog\Processor\MemoryPeakUsageProcessor; use Monolog\Processor\MemoryUsageProcessor; class RotateDriver implements ManageLog { protected $logger; public function __construct() { $logger = new Logger('manager'); $rotatingHandler = new RotatingFileHandler(storage_path('logs/test/manager.log'), 7); $logger->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); } } <?php namespace App\Drivers\Log; use App\Contracts\ManageLog; use Monolog\Logger; use Monolog\Handler\StreamHandler; class StreamDriver implements ManageLog { protected $logger; public function __construct() { $logger = new Logger('manager'); $streamHandler = new StreamHandler(storage_path('logs/test/manager.log')); $logger->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 <?php namespace App\Providers; use Illuminate\Support\ServiceProvider; use App\Components\Log\LogManager; use App\Drivers\Log\StreamDriver; use App\Drivers\Log\RotateDriver; class LogManagerServiceProvider extends ServiceProvider { /** * Register services. * * @return void */ public function register() { $this->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。
今天没有下集预告,发现错误欢迎指正,感谢!!!