PHP设计模式之依赖注入

服务容器,也叫IOC容器,其实包含了依赖注入(DI)和控制反转(IOC)两部分,是laravel的真正核心。其他的各种功能模块比如 Route(路由)、Eloquent ORM(数据库 ORM 组件)、Request and Response(请求和响应)等等等等,实际上都是与核心无关的类模块提供的,这些类从注册到实例化,最终被你所使用,其实都是 laravel 的服务容器负责的。服务容器这个概念比较难解释清楚,只能一步步从服务容器的产生历史慢慢解释。

 

现在我们假设有个需求,需要一个“超人”模块,不同的超人拥有不同的超能力。

IoC 容器诞生的故事——石器时代(原始模式)

首先,我们创建一个超人类:

class Superman {}

然后,再创建一个能力类:

class Power {
    /**
     * 能力值
     */
    protected $ability;

    /**
     * 能力范围或距离
     */
    protected $range;

    public function __construct($ability, $range)
    {
        $this->ability = $ability;
        $this->range = $range;
    }
} 

再回过头修改超人类:

class Superman
{
    protected $power;

    public function __construct()
    {
        $this->power = new Power(999, 100);
    }
}

这时候,一个简单的超人类实现了,且这个过程中,超人类和能力产生了依赖。

实例化后的超人,会拥有一个超能力。但是,不同的超人应当有不同的超能力

如:

/**
*飞行能力
*/
class Flight
{
    protected $speed; //飞行速度
    protected $holdtime; //持续飞行时间
    public function __construct($speed, $holdtime) {}
}

/**
*力量
*/
class Force
{
    protected $force; //力量值
    public function __construct($force) {}
}

/**
*能量弹
*/
class Shot
{
    protected $atk; //伤害
    protected $range; //射击距离
    protected $limit; //同时射击个数
    public function __construct($atk, $range, $limit) {}
} 

现在重构超人类:

class Superman
{
    protected $power;

    public function __construct()
    {
        $this->power = new Fight(9, 100);
        // $this->power = new Force(45);
        // $this->power = new Shot(99, 50, 2);
    }
}

现在可以看到,这个写法已经不足以支撑这个需求了,必须要在实例化能力类的时候加判断了,也就是下面要说到的工厂模式。

 

IoC 容器诞生的故事——青铜时代(工厂模式)

给能力加个制作工厂:

class SuperModuleFactory
{
    public function makeModule($moduleName, $options)
    {
        switch ($moduleName) {
            case 'Fight':     return new Fight($options[0], $options[1]);
            case 'Force':     return new Force($options[0]);
            case 'Shot':     return new Shot($options[0], $options[1], $options[2]);
        }
    }
}

重构超人类:

class Superman
{
    protected $power;

    public function __construct()
    {
        // 初始化工厂
        $factory = new SuperModuleFactory();

        // 通过工厂提供的方法制造需要的模块
        $this->power = $factory->makeModule('Fight', [9, 100]);
    }
}

这时,可以看到,能力类和超人已经成功解耦了,依赖关系变成了超人和能力工厂,能力的增减已经不用动超人类了。

现在需求需要超人和能力关系变成一对多。改造超人类,也是体现工厂模式的价值时候:

class Superman
{
    protected $power;

    public function __construct(array $modules)
    {
        // 初始化工厂
        $factory = new SuperModuleFactory;

        // 通过工厂提供的方法制造需要的模块
        foreach ($modules as $moduleName => $moduleOptions) {
            $this->power[] = $factory->makeModule($moduleName, $moduleOptions);
        }
    }
}

// 创建超人
$superman = new Superman([
    'Fight' => [9, 100], 
    'Shot' => [99, 50, 2]
    ]);

到这里,其实已经足以支撑大部分模块的需求了。

但是在此需求中,随着能力的不断增加:

class SuperModuleFactory
{
    public function makeModule($moduleName, $options)
    {
        switch ($moduleName) {
            case 'Fight':     return new Fight($options[0], $options[1]);
            case 'Force':     return new Force($options[0]);
            case 'Shot':     return new Shot($options[0], $options[1], $options[2]);
            // case 'more': .......
            // case 'and more': .......
            // case 'and more': .......
            // case 'and more': .......
            // case 'and more': .......
            // case 'and more': .......
            // case 'and more': .......
        }
    }
}

工厂变成了相当臃肿的一个配置文件。最简单的解决变法:

class SuperModuleFactory
{
    public function makeModule($fileName, $options)
    {
        return new $fileName($options);
    }
}

这是个完美又可惜的解决方法,这个方法有效的解决了上述臃肿问题,但是依赖却加强了。且,看着这个方法,最直观的感受是不是就是有更好的办法? 其实这已经是半个依赖注入了,我们需要做的只是优化这个方案:把实例化操作接耦。

 

IoC 容器诞生的故事——铁器时代(依赖注入)

由于对超能力模组的需求不断增大,也变的越来越不可靠。我们应当给能力组,定义一个生产接口。不管谁生产的能力,要符合这个接口才会被使用。

interface SuperModuleInterface
{
    /**
     * 超能力激活方法
     *
     * 任何一个超能力都得有该方法,并拥有一个参数
     *@param array $target 针对目标,可以是一个或多个,自己或他人
     */
    public function activate(array $target);
}

遵循这个接口,生产超能力:

/**
 * X-超能量
 */
class XPower implements SuperModuleInterface
{
    public function activate(array $target)
    {
        // 这只是个例子。。具体自行脑补
    }
}

/**
 * 终极炸弹 (就这么俗)
 */
class UltraBomb implements SuperModuleInterface
{
    public function activate(array $target)
    {
        // 这只是个例子。。具体自行脑补
    }
}

同时在超人类加上约束,不接受非此接口的能力。

class Superman
{
    protected $module;

    public function __construct(SuperModuleInterface $module)
    {
        $this->module = $module
    }
}

到此,超人类和能力才正式完成解耦,能力类不管怎么折腾,都不会影响到超人类。

且,一个简单的依赖注入模式也完成了。没有工厂做桥梁, 类与类之间也没有任何耦合。

// 超能力模组
$superModule = new XPower();

// 初始化一个超人,并注入一个超能力模组依赖
$superMan = new Superman($superModule);

依赖注入:只要不是由内部生产(比如初始化、构造函数 __construct 中通过工厂方法、自行手动 new 的),而是由外部以参数或其他形式注入的,都属于 依赖注入(DI) 。

这个例子依赖注入模式了,耦合非常底。 看着这个可以说已经很完美了。

但是,当你实际操作的时候就会发现,完全不是这么回事,要手动一个个实例化类。还没有上述的工厂模式好用。那怎么办呢?

呵呵,加个工厂呗!

.....

 

IoC 容器诞生的故事——科技时代(IoC容器)

这时,我们需要一个更高级的工厂来实现自动化。

这种更为高级的工厂,就是工厂模式的升华 —— IoC 容器。

class Container
{
    protected $binds;

    protected $instances;

    public function bind($abstract, $concrete)
    {
        if ($concrete instanceof Closure) {
            $this->binds[$abstract] = $concrete;
        } else {
            $this->instances[$abstract] = $concrete;
        }
    }

    public function make($abstract, $parameters = [])
    {
        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }

        array_unshift($parameters, $this);

        return call_user_func_array($this->binds[$abstract], $parameters);
    }
}

这时候,一个十分粗糙的容器就诞生了。现在的确很简陋,但不妨碍我们进一步提升他。先着眼现在,看看这个容器如何使用吧!

// 创建一个容器(后面称作超级工厂)
$container = new Container;

// 向该 超级工厂 添加 超人 的生产脚本
$container->bind('superman', function($container, $moduleName) {
    return new Superman($container->make($moduleName));
});

// 向该 超级工厂 添加 超能力模组 的生产脚本
$container->bind('xpower', function($container) {
    return new XPower;
});

// 同上
$container->bind('ultrabomb', function($container) {
    return new UltraBomb;
});

// ******************  华丽丽的分割线  **********************
// 开始启动生产
$superman_1 = $container->make('superman', 'xpower');
$superman_2 = $container->make('superman', 'ultrabomb');
$superman_3 = $container->make('superman', 'xpower');
// ...随意添加

我们彻底的解除了 超人 与 超能力模组 的依赖关系,更重要的是,容器类也丝毫没有和他们产生任何依赖!

用绑定操作,来注册一些生产脚本,再用make来做具体的实例化的出发。

这样一种方式,使得我们更容易在创建一个实例的同时解决其依赖关系,并且更加灵活。当有新的需求,只需另外绑定一个“生产脚本”即可。

实际上,真正的 IoC 容器更为高级。我们现在的例子中,还是需要手动提供超人所需要的模组参数,但真正的 IoC 容器会根据类的依赖需求,自动在注册、绑定的一堆实例中搜寻符合的依赖需求,并自动注入。

 

laravel初始化一个服务容器的大概过程

对于laravel初始化服务容器的过程,还是以代码加注释的方式来大致的解释一下,初始化过程都做了什么工作 /public/index.php文件里面有一行初始化服务器容器的代码,调度的相关文件就是:/bootstrap/app.php

代码清单/bootstrap/app.php

//真正的初始化服务容器代码,下面有此行的继续追踪
$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);
//单例一个App\Http\Kernel对象,可以使用App::make('Illuminate\Contracts\Http\Kernel')调用
$app->singleton(
    'Illuminate\Contracts\Http\Kernel',
    'App\Http\Kernel'
);
//单例一个App\Console\Kernel对象,可以使用App::make('Illuminate\Contracts\Console\Kernel')调用
$app->singleton(
    'Illuminate\Contracts\Console\Kernel',
    'App\Console\Kernel'
);
//同上
$app->singleton(
    'Illuminate\Contracts\Debug\ExceptionHandler',
    'App\Exceptions\Handler'
);
//返回一个初始化完成的服务容器
return $app;

代码清单Illuminate\Foundation\Application

//代码太多,只能解释几个主要的方法(真实情况是,我了解也不多,也就看了这几个方法*^_^*)">//代码太多,只能解释几个主要的方法(真实情况是,我了解也不多,也就看了这几个方法*^_^*)
public function __construct($basePath = null)
    {
        //初始化最简单的容器
        $this->registerBaseBindings();
        //在容器中注册最基本的服务提供者(即ServiceProvider)
        $this->registerBaseServiceProviders();
        //在容器中注册一些核心类的别名(这个说法貌似有点不妥,可以参见以下的代码注释自己再理解一下)
        $this->registerCoreContainerAliases();
        //在容器中注册一些常用的文档绝对路径
        if ($basePath) $this->setBasePath($basePath);
    }

    protected function registerBaseBindings()
    {
        //初始化一个空的容器
        static::setInstance($this);
        //在容器中,实例化一个key为app的实例,相对的值就是当前容器,你可以使用App::make('app')来取得一个容器对象
        $this->instance('app', $this);
        //同上
        $this->instance('Illuminate\Container\Container', $this);
    }

    protected function registerBaseServiceProviders()
    {
        //EventServiceProvider这个服务提供者,其实是向容器注册了一个key为events的对象,可以在你的IDE里面追踪一下代码
        $this->register(new EventServiceProvider($this));
        //注册4个key分别为router、url、redirect、Illuminate\Contracts\Routing\ResponseFactory的对象
        $this->register(new RoutingServiceProvider($this));
    }

    /*这个方法的作用,就以一个例子来解释吧(语文不太好~\(≧▽≦)/~)
        在调用此方法之前,我们想取得一个容器实例的做法是 App::make('app');
        现在我们可以使用App::make('Illuminate\Foundation\Application')
        App::make('Illuminate\Contracts\Container\Container')
        App::make('Illuminate\Contracts\Foundation\Application')
        三种方法来取得一个容器实例,即Illuminate\Foundation\Application、Illuminate\Contracts\Container\Container、Illuminate\Contracts\Foundation\Application三者都是app的别名;
    */
    public function registerCoreContainerAliases()
    {
        $aliases = array(
            'app'                  => ['Illuminate\Foundation\Application', 'Illuminate\Contracts\Container\Container', 'Illuminate\Contracts\Foundation\Application'],
            'artisan'              => ['Illuminate\Console\Application', 'Illuminate\Contracts\Console\Application'],
            'auth'                 => 'Illuminate\Auth\AuthManager',
            'auth.driver'          => ['Illuminate\Auth\Guard', 'Illuminate\Contracts\Auth\Guard'],
            'auth.password.tokens' => 'Illuminate\Auth\Passwords\TokenRepositoryInterface',
            'blade.compiler'       => 'Illuminate\View\Compilers\BladeCompiler',
            'cache'                => ['Illuminate\Cache\CacheManager', 'Illuminate\Contracts\Cache\Factory'],
            'cache.store'          => ['Illuminate\Cache\Repository', 'Illuminate\Contracts\Cache\Repository'],
            'config'               => ['Illuminate\Config\Repository', 'Illuminate\Contracts\Config\Repository'],
            'cookie'               => ['Illuminate\Cookie\CookieJar', 'Illuminate\Contracts\Cookie\Factory', 'Illuminate\Contracts\Cookie\QueueingFactory'],
            'encrypter'            => ['Illuminate\Encryption\Encrypter', 'Illuminate\Contracts\Encryption\Encrypter'],
            'db'                   => 'Illuminate\Database\DatabaseManager',
            'events'               => ['Illuminate\Events\Dispatcher', 'Illuminate\Contracts\Events\Dispatcher'],
            'files'                => 'Illuminate\Filesystem\Filesystem',
            'filesystem'           => 'Illuminate\Contracts\Filesystem\Factory',
            'filesystem.disk'      => 'Illuminate\Contracts\Filesystem\Filesystem',
            'filesystem.cloud'     => 'Illuminate\Contracts\Filesystem\Cloud',
            'hash'                 => 'Illuminate\Contracts\Hashing\Hasher',
            'translator'           => ['Illuminate\Translation\Translator', 'Symfony\Component\Translation\TranslatorInterface'],
            'log'                  => ['Illuminate\Log\Writer', 'Illuminate\Contracts\Logging\Log', 'Psr\Log\LoggerInterface'],
            'mailer'               => ['Illuminate\Mail\Mailer', 'Illuminate\Contracts\Mail\Mailer', 'Illuminate\Contracts\Mail\MailQueue'],
            'paginator'            => 'Illuminate\Pagination\Factory',
            'auth.password'        => ['Illuminate\Auth\Passwords\PasswordBroker', 'Illuminate\Contracts\Auth\PasswordBroker'],
            'queue'                => ['Illuminate\Queue\QueueManager', 'Illuminate\Contracts\Queue\Factory', 'Illuminate\Contracts\Queue\Monitor'],
            'queue.connection'     => 'Illuminate\Contracts\Queue\Queue',
            'redirect'             => 'Illuminate\Routing\Redirector',
            'redis'                => ['Illuminate\Redis\Database', 'Illuminate\Contracts\Redis\Database'],
            'request'              => 'Illuminate\Http\Request',
            'router'               => ['Illuminate\Routing\Router', 'Illuminate\Contracts\Routing\Registrar'],
            'session'              => 'Illuminate\Session\SessionManager',
            'session.store'        => ['Illuminate\Session\Store', 'Symfony\Component\HttpFoundation\Session\SessionInterface'],
            'url'                  => ['Illuminate\Routing\UrlGenerator', 'Illuminate\Contracts\Routing\UrlGenerator'],
            'validator'            => ['Illuminate\Validation\Factory', 'Illuminate\Contracts\Validation\Factory'],
            'view'                 => ['Illuminate\View\Factory', 'Illuminate\Contracts\View\Factory'],
        );

        foreach ($aliases as $key => $aliases)
        {
            foreach ((array) $aliases as $alias)
            {
                $this->alias($key, $alias);
            }
        }
    }

由此得到的一个容器实例:

Application {#2 ▼">Application {#2 ▼
  #basePath: "/Applications/XAMPP/xamppfiles/htdocs/laravel"
  #hasBeenBootstrapped: false
  #booted: false
  #bootingCallbacks: []
  #bootedCallbacks: []
  #terminatingCallbacks: []
  #serviceProviders: array:2 [▶]
  #loadedProviders: array:2 [▶]
  #deferredServices: []
  #storagePath: null
  #environmentFile: ".env"
  #resolved: array:1 [▶]
  #bindings: array:8 [▼
    "events" => array:2 [▶]
    "router" => array:2 [▶]
    "url" => array:2 [▶]
    "redirect" => array:2 [▶]
    "Illuminate\Contracts\Routing\ResponseFactory" => array:2 [▶]
    "Illuminate\Contracts\Http\Kernel" => array:2 [▶]
    "Illuminate\Contracts\Console\Kernel" => array:2 [▶]
    "Illuminate\Contracts\Debug\ExceptionHandler" => array:2 [▶]
  ]
  #instances: array:10 [▼
    "app" => Application {#2}
    "Illuminate\Container\Container" => Application {#2}
    "events" => Dispatcher {#5 ▶}
    "path" => "/Applications/XAMPP/xamppfiles/htdocs/laravel/app"
    "path.base" => "/Applications/XAMPP/xamppfiles/htdocs/laravel"
    "path.config" => "/Applications/XAMPP/xamppfiles/htdocs/laravel/config"
    "path.database" => "/Applications/XAMPP/xamppfiles/htdocs/laravel/database"
    "path.lang" => "/Applications/XAMPP/xamppfiles/htdocs/laravel/resources/lang"
    "path.public" => "/Applications/XAMPP/xamppfiles/htdocs/laravel/public"
    "path.storage" => "/Applications/XAMPP/xamppfiles/htdocs/laravel/storage"
  ]
  #aliases: array:59 [▼
    "Illuminate\Foundation\Application" => "app"
    "Illuminate\Contracts\Container\Container" => "app"
    "Illuminate\Contracts\Foundation\Application" => "app"
    "Illuminate\Console\Application" => "artisan"
    "Illuminate\Contracts\Console\Application" => "artisan"
    "Illuminate\Auth\AuthManager" => "auth"
    "Illuminate\Auth\Guard" => "auth.driver"
    "Illuminate\Contracts\Auth\Guard" => "auth.driver"
    "Illuminate\Auth\Passwords\TokenRepositoryInterface" => "auth.password.tokens"
    "Illuminate\View\Compilers\BladeCompiler" => "blade.compiler"
    "Illuminate\Cache\CacheManager" => "cache"
    "Illuminate\Contracts\Cache\Factory" => "cache"
    "Illuminate\Cache\Repository" => "cache.store"
    "Illuminate\Contracts\Cache\Repository" => "cache.store"
    "Illuminate\Config\Repository" => "config"
    "Illuminate\Contracts\Config\Repository" => "config"
    "Illuminate\Cookie\CookieJar" => "cookie"
    "Illuminate\Contracts\Cookie\Factory" => "cookie"
    "Illuminate\Contracts\Cookie\QueueingFactory" => "cookie"
    "Illuminate\Encryption\Encrypter" => "encrypter"
    "Illuminate\Contracts\Encryption\Encrypter" => "encrypter"
    "Illuminate\Database\DatabaseManager" => "db"
    "Illuminate\Events\Dispatcher" => "events"
    "Illuminate\Contracts\Events\Dispatcher" => "events"
    "Illuminate\Filesystem\Filesystem" => "files"
    "Illuminate\Contracts\Filesystem\Factory" => "filesystem"
    "Illuminate\Contracts\Filesystem\Filesystem" => "filesystem.disk"
    "Illuminate\Contracts\Filesystem\Cloud" => "filesystem.cloud"
    "Illuminate\Contracts\Hashing\Hasher" => "hash"
    "Illuminate\Translation\Translator" => "translator"
    "Symfony\Component\Translation\TranslatorInterface" => "translator"
    "Illuminate\Log\Writer" => "log"
    "Illuminate\Contracts\Logging\Log" => "log"
    "Psr\Log\LoggerInterface" => "log"
    "Illuminate\Mail\Mailer" => "mailer"
    "Illuminate\Contracts\Mail\Mailer" => "mailer"
    "Illuminate\Contracts\Mail\MailQueue" => "mailer"
    "Illuminate\Pagination\Factory" => "paginator"
    "Illuminate\Auth\Passwords\PasswordBroker" => "auth.password"
    "Illuminate\Contracts\Auth\PasswordBroker" => "auth.password"
    "Illuminate\Queue\QueueManager" => "queue"
    "Illuminate\Contracts\Queue\Factory" => "queue"
    "Illuminate\Contracts\Queue\Monitor" => "queue"
    "Illuminate\Contracts\Queue\Queue" => "queue.connection"
    "Illuminate\Routing\Redirector" => "redirect"
    "Illuminate\Redis\Database" => "redis"
    "Illuminate\Contracts\Redis\Database" => "redis"
    "Illuminate\Http\Request" => "request"
    "Illuminate\Routing\Router" => "router"
    "Illuminate\Contracts\Routing\Registrar" => "router"
    "Illuminate\Session\SessionManager" => "session"
    "Illuminate\Session\Store" => "session.store"
    "Symfony\Component\HttpFoundation\Session\SessionInterface" => "session.store"
    "Illuminate\Routing\UrlGenerator" => "url"
    "Illuminate\Contracts\Routing\UrlGenerator" => "url"
    "Illuminate\Validation\Factory" => "validator"
    "Illuminate\Contracts\Validation\Factory" => "validator"
    "Illuminate\View\Factory" => "view"
    "Illuminate\Contracts\View\Factory" => "view"
  ]
  #extenders: []
  #tags: []
  #buildStack: []
  +contextual: []
  #reboundCallbacks: []
  #globalResolvingCallbacks: []
  #globalAfterResolvingCallbacks: []
  #resolvingCallbacks: []
  #afterResolvingCallbacks: []
}

怎么打印一个实例??
到这一步为止,你可以这样做dd(app())

dd(app())什么意思??
这里包含两个方法dd()和app(),具体定义请看自动加载的第四种方法

那说好的App::make(‘app’)方法咋不能用呢?
这是因为这个方法需要用到Contracts,而到此为止,还未定义App作为Illuminate\Support\Facades\App的别名,因而不能用;需要等到统一入口文件里面的运行Kernel类的handle方法才能用,所以在Controller里面是可以用的,现在不能用

到此为止,一个容器实例就诞生了,事情就是这么个事情,情况就是这个个情况,再具体的那就需要你自己去看代码了,我知道的就这些。

 

启动Kernel代码 
Kernel实例调用handle方法,意味着laravel的核心和公用代码已经准备完毕,此项目正式开始运行
代码清单/app/Http/Kernel.php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel {

    //这是在调用路由之前需要启动的中间件,一般都是核心文件,不要修改
    protected $middleware = [
        'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
        'Illuminate\Cookie\Middleware\EncryptCookies',
        'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
        'Illuminate\Session\Middleware\StartSession',
        'Illuminate\View\Middleware\ShareErrorsFromSession',
        'App\Http\Middleware\VerifyCsrfToken',
    ];

    //这是我们在router.php文件里面或者Controller文件里面,可以使用的Middleware元素,可以自定义加入很多
    protected $routeMiddleware = [
        'auth' => 'App\Http\Middleware\Authenticate',
        'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
        'guest' => 'App\Http\Middleware\RedirectIfAuthenticated',
        'test' => 'App\Http\Middleware\testMiddleWare',
    ];

}

大家看到了,其实这个文件里面没有handle方法,只有一些属性定义,所以真正的handle方法,实在父类里面实现的

代码清单…/Illuminate/Foundation/Http/Kernel.php
//好多代码,见几个我看过的扯扯,其他的期待你们补上">//好多代码,见几个我看过的扯扯,其他的期待你们补上

//这个很重要,是项目的一些启动引导项,Kernel的重要步骤中,首先就是启动这些文件的bootstrap方法
protected $bootstrappers = [
        //检测环境变量文件是否正常
        'Illuminate\Foundation\Bootstrap\DetectEnvironment',
        //取得配置文件,即把/config/下的所有配置文件读取到容器(app()->make('config')可以查看所有配置信息)
        'Illuminate\Foundation\Bootstrap\LoadConfiguration',
        //绑定一个名字为log的实例到容器,怎么访问??(app()->make('log'))
        'Illuminate\Foundation\Bootstrap\ConfigureLogging',
        //设置异常抓取信息,这个还没仔细看,但大概就是这个意思
        'Illuminate\Foundation\Bootstrap\HandleExceptions',
        //把/config/app.php里面的aliases项利用PHP库函数class_alias创建别名,从此,我们可以使用App::make('app')方式取得实例
        'Illuminate\Foundation\Bootstrap\RegisterFacades',
        //把/config/app.php里面的providers项,注册到容器
        'Illuminate\Foundation\Bootstrap\RegisterProviders',
        //运行容器中注册的所有的ServiceProvider中得boot方法
        'Illuminate\Foundation\Bootstrap\BootProviders',
    ];

  //真正的handle方法
  public function handle($request)
    {
        try
        {
            //主要是这行,调度了需要运行的方法
            return $this->sendRequestThroughRouter($request);
        }
        catch (Exception $e)
        {
            $this->reportException($e);

            return $this->renderException($request, $e);
        }
    }


    protected function sendRequestThroughRouter($request)
    {
        $this->app->instance('request', $request);
        Facade::clearResolvedInstance('request');
        //运行上述$bootstrappers里面包含的文件的bootstrap方法,运行的作用,上面已经注释
        $this->bootstrap();
        //这是在对URL进行调度之前,也就是运行Route之前,进行的一些准备工作
        return (new Pipeline($this->app))    //不解释
                    ->send($request)        //继续不解释
                    //需要运行$this->middleware里包含的中间件
                    ->through($this->middleware)
                    //运行完上述中间件之后,调度dispatchToRouter方法,进行Route的操作
                    ->then($this->dispatchToRouter());
    }

    //前奏执行完毕之后,进行Route操作
    protected function dispatchToRouter()
    {
        return function($request)
        {
            $this->app->instance('request', $request);
            //跳转到Router类的dispatch方法
            return $this->router->dispatch($request);
        };
    }

下面就需要根据URL和/app/Http/routes.php文件,进行Route操作

 下面就需要根据URL和/app/Http/routes.php文件,进行Route操作

//代码好多,挑几个解释">//代码好多,挑几个解释

    public function dispatch(Request $request)
    {
        $this->currentRequest = $request;
        //在4.2版本里面,Route有一个筛选属性;5.0之后的版本,被Middleware代替
        $response = $this->callFilter('before', $request);

        if (is_null($response))
        {    
            //继续调度
            $response = $this->dispatchToRoute($request);
        }

        $response = $this->prepareResponse($request, $response);
        //在4.2版本里面,Route有一个筛选属性;5.0之后的版本,被Middleware代替
        $this->callFilter('after', $request, $response);

        return $response;
    }

    public function dispatchToRoute(Request $request)
    {
        $route = $this->findRoute($request);
        $request->setRouteResolver(function() use ($route)
        {
            return $route;
        });

        $this->events->fire('router.matched', [$route, $request]);
        $response = $this->callRouteBefore($route, $request);

        if (is_null($response))
        {
            // 只看这一行,还是调度文件
            $response = $this->runRouteWithinStack(
                $route, $request
            );
        }

        $response = $this->prepareResponse($request, $response);
        $this->callRouteAfter($route, $request, $response);

        return $response;
    }

    //干货来了
    protected function runRouteWithinStack(Route $route, Request $request)
    {
        // 取得routes.php里面的Middleware节点
        $middleware = $this->gatherRouteMiddlewares($route);
        //这个有点眼熟
        return (new Pipeline($this->container))
                        ->send($request)
                        //执行上述的中间件
                        ->through($middleware)
                        ->then(function($request) use ($route)
                        {    
                            //不容易啊,终于到Controller类了
                            return $this->prepareResponse(
                                $request,
                                //run控制器
                                $route->run($request)
                            );
                        });
    }

    public function run(Request $request)
    {
        $this->container = $this->container ?: new Container;
        try
        {
            if ( ! is_string($this->action['uses']))
                return $this->runCallable($request);
            if ($this->customDispatcherIsBound())
            //实际上是运行了这行
                return $this->runWithCustomDispatcher($request);

            //其实我是直接想运行这行
            return $this->runController($request);
        }
        catch (HttpResponseException $e)
        {
            return $e->getResponse();
        }
    }

    //继续调度,最终调度到.../Illuminate/Routing/ControllerDispatcher.php文件的dispatch方法
    protected function runWithCustomDispatcher(Request $request)
    {
        list($class, $method) = explode('@', $this->action['uses']);

        $dispatcher = $this->container->make('illuminate.route.dispatcher');
        return $dispatcher->dispatch($this, $request, $class, $method);
    }
文件清单…/Illuminate/Routing/ControllerDispatcher.php
    public function dispatch(Route $route, Request $request, $controller, $method)">    public function dispatch(Route $route, Request $request, $controller, $method)
    {
        $instance = $this->makeController($controller);

        $this->assignAfter($instance, $route, $request, $method);

        $response = $this->before($instance, $route, $request, $method);

        if (is_null($response))
        {
            //还要调度
            $response = $this->callWithinStack(
                $instance, $route, $request, $method
            );
        }

        return $response;
    }

    protected function callWithinStack($instance, $route, $request, $method)
    {
        //又是Middleware......有没有忘记,官方文档里面Middleware可以加在控制器的构造函数中!!没错,这个Middleware就是在控制器里面申明的
        $middleware = $this->getMiddleware($instance, $method);
        //又是这个,眼熟吧
        return (new Pipeline($this->container))
                    ->send($request)
                    //再次运行Middleware
                    ->through($middleware)
                    ->then(function($request) use ($instance, $route, $method)
                    {    
                        运行控制器,返回结果
                        return $this->call($instance, $route, $method);
                    });
    }

这就是从入口文件到控制器中间,进行的一系列操作,也终于到我们干活的地方了。

转载于:https://my.oschina.net/tanwen/blog/1531737

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值