zoukankan      html  css  js  c++  java
  • Laravel驱动管理类Manager的分析和使用

    Laravel驱动管理类Manager的分析和使用

    第一部分 概念说明

    第二部分 IlluminateSupportManager源码

    第三部分 Manager类的使用

    第一部分:概念解释

    结合实际解释一下,啥是驱动:当我点了份外卖,那么外卖小哥无论如何都要讲外卖送到我的手中,我不会关心小哥走的是丝绸之路,还是强者之路,更不会关心他是骑着飞机、坦克还是大炮送来的。我只要我的外卖到我的手中。

    归纳一下,我点外卖要就要得到外卖,这就是契约,这就是接口规定的功能。

    小哥走什么路线,什么交通工具是他自己的实现,也就是各种驱动。

    是不是和laravel的契约和服务提供者概念很相似呢?

    只不过今天要讲解的Manager更加强调管理各个驱动,提前将所有的驱动全部注册好,在使用的时候直接解析或者切换。

    说到这道友们应该理解了,Manager能做的事情Container和Provider能够做的更好。

    那么Manager和Container、Provider的区别在哪呢?这里我只说明我的理解,Container和Provider更加的偏向框架层级,虽然也可以非侵入式的扩展和修改,但是相对Manager稍加麻烦,我将Manager看做一个小型的Container,里面包含了我要实现某个功能的各种驱动(实现),Manager更加的偏向业务逻辑层。

    当要频繁切换一个功能实现的时候(他更像一个频繁更换内容,但是说明书不换的组件,俗话说的换汤不换药),我可能会选择Manager(比如发送短信,可以使用阿里大于,京东万象,飞鸽等等),因为他更加轻量。当要实现一个系统级的服务的时候,我会选择Container和Provider,比如上一篇中的日志服务。

    第二部分:源码说明
    # 直接上代码 挺简单的一个类,基本可以见名知意。未展示属性
    <?php
    
    namespace IlluminateSupport;
    
    use Closure;
    use IlluminateContractsContainerContainer;
    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 AppContracts;
    
    interface ManageLog
    {
        public function logCertains($level, $foo);
    }
    
    2 创建日志组件,使用管理器管理
    <?php
    
    namespace AppComponentsLog;
    
    use IlluminateSupportManager;
    
    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 AppDriversLog;
    
    use AppContractsManageLog;
    use MonologLogger;
    use MonologHandlerRotatingFileHandler;
    use MonologProcessorMemoryPeakUsageProcessor;
    use MonologProcessorMemoryUsageProcessor;
    
    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 AppDriversLog;
    
    use AppContractsManageLog;
    use MonologLogger;
    use MonologHandlerStreamHandler;
    
    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);
        }
    
        public function getLogger()
        {
            return $this->logger;
        }
    }
    
    <?php
    
    namespace AppDriversLog;
    
    use AppContractsManageLog;
    use IlluminateFoundationApplication;
    use MonologLogger;
    use MonologHandlerRedisHandler;
    use PredisClient;
    
    class RedisDriver implements ManageLog
    {   
        protected $logger;
    
        public function __construct(Application $app)
        {
            // $logger = new Logger('manager');
            // $redisClient = new Client('tcp://localhost:6379');
            // $redisHandler = new RedisHandler($redisClient, 'manager');
            // $logger->pushHandler($redisHandler);
            // $this->logger = $logger;
    
            //  利用monolog的重用机制
            $customLoggers = $app->make('logManager')->getCustomCreators();
            $streamLogger = call_user_func(array_shift($customLoggers));
            $redisLogger = $streamLogger->getLogger()->withName('redis');
            $redisClient = new Client('tcp://localhost:6379');
            $redisHandler = new RedisHandler($redisClient, 'manager');
            $redisLogger->pushHandler($redisHandler);
            $this->logger = $redisLogger;
        }
    
        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 AppProviders;
    
    use IlluminateSupportServiceProvider;
    use AppComponentsLogLogManager;
    use AppDriversLogStreamDriver;
    use AppDriversLogRotateDriver;
    use AppDriversLogRedisDriver;
    
    class LogManagerServiceProvider extends ServiceProvider
    {
        /**
         * Register services.
         *
         * @return void
         */
        public function register()
        {   
            $this->app->singleton('logManager', function ($app) {
                # return new LogManager($app);
                // 利用容器帮助我们解决类的依赖
                return $app->make(LogManager::class);
            });
            $this->app->singleton(LogManager::class, 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());
            $this->app[LogManager::class]->extend('redis', function () {
                // return new RedisDriver($this->app);
                return $this->app->make(RedisDriver::class);
            });
        }
    }
    
    5 注册服务
    config/app.php
    ...   
    AppProvidersRouteServiceProvider::class,
    // 注册自定义日志服务
    AppProvidersLogServiceProvider::class,
    // 注册日志管理服务
    AppProvidersLogManagerServiceProvider::class,
    
    6 使用测试 
    Route::get('logmanager', function (LogManager $logManager) { 
        resolve('logManager')->driver('stream')->logCertains('emergency', 'something emergency');
        resolve('logManager')->driver('rotate')->logCertains('debug', 'debug something');
        $logManager->driver('redis')->logCertains('info', 'aaa');
    });
    

    以上代码比较简单,各位领会精神就好,大家可以结合前面说过的facade,仿照laravel原生Log服务实现一个功能一致的log manager。

    今天没有下集预告,发现错误欢迎指正,感谢!!!

  • 相关阅读:
    Leetcode Excel Sheet Column Number
    AlgorithmsI PA2: Randomized Queues and Deques Subset
    AlgorithmsI PA2: Randomized Queues and Deques RandomizedQueue
    AlgorithmsI PA2: Randomized Queues and Deques Deque
    AlgorithmsI Programming Assignment 1: PercolationStats.java
    hdu多校第四场 1003 (hdu6616) Divide the Stones 机智题
    hdu多校第四场 1007 (hdu6620) Just an Old Puzzle 逆序对
    hdu多校第四场1001 (hdu6614) AND Minimum Spanning Tree 签到
    hdu多校第三场 1007 (hdu6609) Find the answer 线段树
    hdu多校第三场 1006 (hdu6608) Fansblog Miller-Rabin素性检测
  • 原文地址:https://www.cnblogs.com/alwayslinger/p/13724361.html
Copyright © 2011-2022 走看看