laravel的啓動過程

laravel的啓動過程

如果沒有使用過類似Yii之類的框架,直接去看laravel,會有點一臉迷糊的感覺,起碼我是這樣的。laravel的啓動過程,也是laravel的核心,對這個過程有一個瞭解,有助於得心應手的使用框架,希望能對大家有點幫助。
提示:在此之前,最好看一下官方文檔,大概知道laravel,再來看這個筆記,這樣效果可能會好一點。

統一入口

laravel框架使用了統一入口,入口文件:/public/index.php

<code class="php" data-origin="" 
&lt;?php"><?php
//自動加載文件設置
require __DIR__.'/../bootstrap/autoload.php';

//初始化服務容器(可以查看一下關於‘服務容器’的相關文檔)
$app = require_once __DIR__.'/../bootstrap/app.php';

//通過服務容器生成一個kernel類的實例(Illuminate\Contracts\Http\Kernel實際上只是一個接口,真正生成的實例是App\Http\Kernel類,至於怎麼把接口和類關聯起來,請查看Contracts相關文檔)
$kernel = $app->make('Illuminate\Contracts\Http\Kernel');

//運行Kernel類的handle方法,主要動作是運行middleware和啓動URL相關的Contrller
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

//控制器返回結果之後的操作,暫時還沒看,以後補上
$response->send();

$kernel->terminate($request, $response);
<code class="php" data-origin=""
相關鏈接

<code class="php" data-origin=""

服務容器
Contracts協議模式




<code class="php" data-origin=""
自動加載文件

<code class="php" data-origin=""

laravel的自動加載,其實也就是Composer的自動加載

<code class="php" data-origin=""

我的理解是,Composer根據聲明的依賴關係,從相關庫的源下載代碼文件,並根據依賴關係在 Composer 目錄下生成供類自動加載的 PHP 腳本,使用的時候,項目開始處引入 “/vendor/autoload.php” 文件,就可以直接實例化這些第三方類庫中的類了。那麼,Composer 是如何實現類的自動加載的呢?接下來,我們從 laravel 的入口文件開始順藤摸瓜往裏跟進,來一睹 Composer 自動加載的奧妙。

<code class="php" data-origin=""

代碼清單/bootstrap/autoload.php

<code class="php" data-origin="" 
<code class="php" data-origin="" 
&lt;?php"><?php
define('LARAVEL_START', microtime(true));

//這就是傳說中Composer的自動加載文件
require __DIR__.'/../vendor/autoload.php';

//Composer自動生成的各個核心類的集合,如果你需要修改一些vendor裏面的文件來查看一些laravel運行細節,那麼就請刪除此文件
$compiledPath = __DIR__.'/../vendor/compiled.php';

if (file_exists($compiledPath))
{
    require $compiledPath;
}

<code class="php" data-origin=""

<code class="php" data-origin="" 
代碼清單 laravel/vendor/autoload.php

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
&lt;?php"><?php

// autoload.php @generated by Composer

require_once __DIR__ . '/composer' . '/autoload_real.php';
//別被嚇到了,他就是autoload_real.php文件的類名而已
return ComposerAutoloaderInit03dc6c3c47809c398817ca33ec5f6a01::getLoader();

<code class="php" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
代碼清單laravel/vendor/composer/autoload_real.php:
主要是getLoader方法裏面,加了註釋的幾行,這是關鍵

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
&lt;?php"><?php

// autoload_real.php @generated by Composer

class ComposerAutoloaderInit03dc6c3c47809c398817ca33ec5f6a01
{
    private static $loader;

    public static function loadClassLoader($class)
    {
        if ('Composer\Autoload\ClassLoader' === $class) {
            require __DIR__ . '/ClassLoader.php';
        }
    }

    public static function getLoader()
    {
        if (null !== self::$loader) {
            return self::$loader;
        }

        spl_autoload_register(array('ComposerAutoloaderInit03dc6c3c47809c398817ca33ec5f6a01', 'loadClassLoader'), true, true);
        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
        spl_autoload_unregister(array('ComposerAutoloaderInit03dc6c3c47809c398817ca33ec5f6a01', 'loadClassLoader'));

        $includePaths = require __DIR__ . '/include_paths.php';
        array_push($includePaths, get_include_path());
        set_include_path(join(PATH_SEPARATOR, $includePaths));

        //Psr0標準-設置命名空間對應的路徑,以便於隨後自動加載相關類文件(看看psr0和psr4的區別)
        $map = require __DIR__ . '/autoload_namespaces.php';
        foreach ($map as $namespace => $path) {
            $loader->set($namespace, $path);
        }
        //Psr4標準-設置命名空間對應的路徑,以便於隨後自動加載相關類文件(看看psr0和psr4的區別)
        $map = require __DIR__ . '/autoload_psr4.php';
        foreach ($map as $namespace => $path) {
            $loader->setPsr4($namespace, $path);
        }

        //設置類文件路徑與類名的對應關係,以便於隨後自動加載相關類文件(可能你有一部分類,由於歷史原因,他們的命名空間不遵守PSR0和PSR4,你就可以使用此方法自動加載)
        $classMap = require __DIR__ . '/autoload_classmap.php';
        if ($classMap) {
            $loader->addClassMap($classMap);
        }

        //根據上述三種方法註冊自動加載文檔的方法,可以查看一下PHP的spl_autoload_register和__autoload方法
        $loader->register(true);

        //加載公用方法,比如app()方法取得一個application實例,就是這裏加載的,可以查看一下autoload_files.php文件都加載了什麼公用方法,有很多關於 array的操作方法哦
        $includeFiles = require __DIR__ . '/autoload_files.php';
        foreach ($includeFiles as $file) {
            composerRequire03dc6c3c47809c398817ca33ec5f6a01($file);
        }

        return $loader;
    }
}

function composerRequire03dc6c3c47809c398817ca33ec5f6a01($file)
{
    require $file;
}
<code class="php" data-origin=""
<code class="php" data-origin="" 
<code class="php" data-origin="" 
對於laravel自動加載過程的總結

<code class="php" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
laravel自動加載的過程就是這樣實現的,總結爲四種加載方式:

  1. <code class="php" data-origin=""
    <code class="php" data-origin="" 
    <code class="php" data-origin="" 
    PSR0加載方式—對應的文件就是autoload_namespaces.php
  2. <code class="php" data-origin=""
    <code class="php" data-origin="" 
    <code class="php" data-origin="" 
    PSR4加載方式—對應的文件就是autoload_psr4.php
  3. <code class="php" data-origin=""
    <code class="php" data-origin="" 
    <code class="php" data-origin="" 
    其他加載類的方式—對應的文件就是autoload_classmap.php
  4. <code class="php" data-origin=""
    <code class="php" data-origin="" 
    <code class="php" data-origin="" 
    加載公用方法—對應的文件就是autoload_files.php
<code class="php" data-origin=""
<code class="php" data-origin="" 
<code class="php" data-origin="" 
怎麼樣自定義自動加載方式

<code class="php" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
如果某些文件,需要自動自定義加載方式,可以在Composer.json文件中定義

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="javascript" data-origin="" 
"autoload" : {">"autoload" : {
        //以第一種方式自動加載,表示app目錄下的所有類的命名空間都是以Apppsr0開始且遵循psr0規範(注意:您的laravel中沒有此項,作爲示意例子)
        "psr-0" : {
            "AppPsr0": "apppsr0/"
        },
        //以第二種方式自動加載,表示app目錄下的所有類的命名空間都是以App開始且遵循psr4規範
        "psr-4" : {
            "App\\": "app/"
        },
        //以第三種加載方式自動加載,它會將所有.php和.inc文件中的類提出出來然後以類名作爲key,類的路徑作爲值
        "classmap" : ["database"],
        //以第四種加載方式自動加載,composer會把這些文件都include進來(注意:您的laravel中沒有此項,作爲示意例子)
        "files" : ["common/util.php"]

    }
<code class="php" data-origin=""
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="javascript" data-origin="" 
相關文檔鏈接

<code class="php" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="javascript" data-origin="" 
Compposer中文文檔




<code class="php" data-origin=""
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="javascript" data-origin="" 
服務容器——laravel真正的核心

<code class="php" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="javascript" data-origin="" 
服務容器,也叫IOC容器,其實包含了依賴注入(DI)和控制反轉(IOC)兩部分,是laravel的真正核心。其他的各種功能模塊比如 Route(路由)、Eloquent ORM(數據庫 ORM 組件)、Request and Response(請求和響應)等等等等,實際上都是與核心無關的類模塊提供的,這些類從註冊到實例化,最終被你所使用,其實都是 laravel 的服務容器負責的。服務容器這個概念比較難解釋清楚,只能一步步從服務容器的產生歷史慢慢解釋

<code class="php" data-origin=""
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="javascript" data-origin="" 
IoC 容器誕生的故事——石器時代(原始模式)

<code class="php" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="javascript" data-origin="" 
我們把一個“超人”作爲一個類,

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
class Superman {}">class Superman {}

<code class="php" data-origin=""

<code class="php" data-origin="" 
<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
我們可以想象,一個超人誕生的時候肯定擁有至少一個超能力,這個超能力也可以抽象爲一個對象,爲這個對象定義一個描述他的類吧。一個超能力肯定有多種屬性、(操作)方法,這個盡情的想象,但是目前我們先大致定義一個只有屬性的“超能力”,至於能幹啥,我們以後再豐富:

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
class Power {">class Power {
    /**
     * 能力值
     */
    protected $ability;

    /**
     * 能力範圍或距離
     */
    protected $range;

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

<code class="php" data-origin=""

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
這時候我們回過頭,修改一下之前的“超人”類,讓一個“超人”創建的時候被賦予一個超能力:

<code class="php" data-origin="" 
<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
class Superman">class Superman
{
    protected $power;

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

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
這樣的話,當我們創建一個“超人”實例的時候,同時也創建了一個“超能力”的實例,但是,我們看到了一點,“超人”和“超能力”之間不可避免的產生了一個依賴。

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
所謂“依賴”,就是“我若依賴你,少了你就沒有我”。

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
在一個貫徹面向對象編程的項目中,這樣的依賴隨處可見。少量的依賴並不會有太過直觀的影響,我們隨着這個例子逐漸鋪開,讓大家慢慢意識到,當依賴達到一個量級時,是怎樣一番噩夢般的體驗。當然,我也會自然而然的講述如何解決問題。

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
之前的例子中,超能力類實例化後是一個具體的超能力,但是我們知道,超人的超能力是多元化的,每種超能力的方法、屬性都有不小的差異,沒法通過一種類描述完全。我們現在進行修改,我們假設超人可以有以下多種超能力:
飛行,屬性有:飛行速度、持續飛行時間
蠻力,屬性有:力量值
能量彈,屬性有:傷害值、射擊距離、同時射擊個數
我們創建瞭如下類:

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
class Flight">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) {}
}

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
好了,這下我們的超人有點“忙”了。在超人初始化的時候,我們會根據需要來實例化其擁有的超能力嗎,大致如下:

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
class Superman">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);
        /*
        $this->power = array(
            new Force(45),
            new Shot(99, 50, 2)
        );
        */
    }
}

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
我們需要自己手動的在構造函數內(或者其他方法裏)實例化一系列需要的類,這樣並不好。可以想象,假如需求變更(不同的怪物橫行地球),需要更多的有針對性的 新的 超能力,或者需要 變更 超能力的方法,我們必須 重新改造 超人。換句話說就是,改變超能力的同時,我還得重新制造個超人。效率太低了!新超人還沒創造完成世界早已被毀滅。

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
這時,靈機一動的人想到:爲什麼不可以這樣呢?超人的能力可以被隨時更換,只需要添加或者更新一個芯片或者其他裝置啥的(想到鋼鐵俠沒)。這樣的話就不要整個重新來過了。

<code class="javascript" data-origin=""
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
IoC 容器誕生的故事——青銅時代(工廠模式)

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
我們不應該手動在 “超人” 類中固化了他的 “超能力” 初始化的行爲,而轉由外部負責,由外部創造超能力模組、裝置或者芯片等(我們後面統一稱爲 “模組”),植入超人體內的某一個接口,這個接口是一個既定的,只要這個 “模組” 滿足這個接口的裝置都可以被超人所利用,可以提升、增加超人的某一種能力。這種由外部負責其依賴需求的行爲,我們可以稱其爲 “控制反轉(IoC)”。

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
工廠模式,顧名思義,就是一個類所以依賴的外部事物的實例,都可以被一個或多個 “工廠” 創建的這樣一種開發模式,就是 “工廠模式”。

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
我們爲了給超人制造超能力模組,我們創建了一個工廠,它可以製造各種各樣的模組,且僅需要通過一個方法:

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
class SuperModuleFactory">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]);
        }
    }
}

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
這時候,超人 創建之初就可以使用這個工廠!

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
class Superman">class Superman
{
    protected $power;

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

        // 通過工廠提供的方法制造需要的模塊
        $this->power = $factory->makeModule('Fight', [9, 100]);
        // $this->power = $factory->makeModule('Force', [45]);
        // $this->power = $factory->makeModule('Shot', [99, 50, 2]);
        /*
        $this->power = array(
            $factory->makeModule('Force', [45]),
            $factory->makeModule('Shot', [99, 50, 2])
        );
        */
    }
}

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
可以看得出,我們不再需要在超人初始化之初,去初始化許多第三方類,只需初始化一個工廠類,即可滿足需求。但這樣似乎和以前區別不大,只是沒有那麼多 new 關鍵字。其實我們稍微改造一下這個類,你就明白,工廠類的真正意義和價值了。

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
class Superman">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]
    ]);

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
現在修改的結果令人滿意。現在,“超人” 的創建不再依賴任何一個 “超能力” 的類,我們如若修改了或者增加了新的超能力,只需要針對修改 SuperModuleFactory 即可。擴充超能力的同時不再需要重新編輯超人的類文件,使得我們變得很輕鬆。但是,這纔剛剛開始。

<code class="javascript" data-origin=""
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
IoC 容器誕生的故事——鐵器時代(依賴注入)

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
由 “超人” 對 “超能力” 的依賴變成 “超人” 對 “超能力模組工廠” 的依賴後,對付小怪獸們變得更加得心應手。但這也正如你所看到的,依賴並未解除,只是由原來對多個外部的依賴變成了對一個 “工廠” 的依賴。假如工廠出了點麻煩,問題變得就很棘手。

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
其實大多數情況下,工廠模式已經足夠了。工廠模式的缺點就是:接口未知(即沒有一個很好的契約模型,關於這個我馬上會有解釋)、產生對象類型單一。總之就是,還是不夠靈活。雖然如此,工廠模式依舊十分優秀,並且適用於絕大多數情況。不過我們爲了講解後面的 依賴注入 ,這裏就先誇大一下工廠模式的缺陷咯。

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
我們知道,超人依賴的模組,我們要求有統一的接口,這樣才能和超人身上的注入接口對接,最終起到提升超能力的效果。事實上,我之前說謊了,不僅僅只有一堆小怪獸,還有更多的大怪獸。嘿嘿。額,這時候似乎工廠的生產能力顯得有些不足 —— 由於工廠模式下,所有的模組都已經在工廠類中安排好了,如果有新的、高級的模組加入,我們必須修改工廠類(好比增加新的生產線):

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
class SuperModuleFactory">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 'oh no! its too many!': .......
        }
    }
}

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
看到沒。。。噩夢般的感受!

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
其實靈感就差一步!你可能會想到更爲靈活的辦法!對,下一步就是我們今天的主要配角 —— DI (依賴注入)

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
由於對超能力模組的需求不斷增大,我們需要集合整個世界的高智商人才,一起解決問題,不應該僅僅只有幾個工廠壟斷負責。不過高智商人才們都非常自負,認爲自己的想法是對的,創造出的超能力模組沒有統一的接口,自然而然無法被正常使用。這時我們需要提出一種契約,這樣無論是誰創造出的模組,都符合這樣的接口,自然就可被正常使用。

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
interface SuperModuleInterface">interface SuperModuleInterface
{
    /**
     * 超能力激活方法
     *
     * 任何一個超能力都得有該方法,並擁有一個參數
     *@param array $target 針對目標,可以是一個或多個,自己或他人
     */
    public function activate(array $target);
}

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
上文中,我們定下了一個接口 (超能力模組的規範、契約),所有被創造的模組必須遵守該規範,才能被生產。
其實,這就是 php 中 接口( interface ) 的用處和意義!
很多人覺得,爲什麼 php 需要接口這種東西?難道不是 java 、 C# 之類的語言纔有的嗎?這麼說,只要是一個正常的面向對象編程語言(雖然 php 可以面向過程),都應該具備這一特性。因爲一個 對象(object) 本身是由他的模板或者原型 —— 類 (class) ,經過實例化後產生的一個具體事物,而有時候,實現統一種方法且不同功能(或特性)的時候,會存在很多的類(class),這時候就需要有一個契約,讓大家編寫出可以被隨時替換卻不會產生影響的接口。這種由編程語言本身提出的硬性規範,會增加更多優秀的特性。

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
這時候,那些提出更好的超能力模組的高智商人才,遵循這個接口,創建了下述(模組)類:

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
/**">/**
 * X-超能量
 */
class XPower implements SuperModuleInterface
{
    public function activate(array $target)
    {
        // 這只是個例子。。具體自行腦補
    }
}

/**
 * 終極炸彈 (就這麼俗)
 */
class UltraBomb implements SuperModuleInterface
{
    public function activate(array $target)
    {
        // 這只是個例子。。具體自行腦補
    }
}

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
同時,爲了防止有些 “磚家” 自作聰明,或者一些叛徒惡意搗蛋,不遵守契約胡亂製造模組,影響超人,我們對超人初始化的方法進行改造:

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
class Superman">class Superman
{
    protected $module;

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

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
改造完畢!現在,當我們初始化 “超人” 類的時候,提供的模組實例必須是一個 SuperModuleInterface 接口的實現。否則就會提示錯誤。

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
正是由於超人的創造變得容易,一個超人也就不需要太多的超能力,我們可以創造多個超人,並分別注入需要的超能力模組即可。這樣的話,雖然一個超人只有一個超能力,但超人更容易變多,我們也不怕怪獸啦!

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
現在有人疑惑了,你要講的 依賴注入 呢?
其實,上面講的內容,正是依賴注入。

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
什麼叫做 依賴注入?
本文從開頭到現在提到的一系列依賴,只要不是由內部生產(比如初始化、構造函數 __construct 中通過工廠方法、自行手動 new 的),而是由外部以參數或其他形式注入的,都屬於 依賴注入(DI) 。是不是豁然開朗?事實上,就是這麼簡單。下面就是一個典型的依賴注入:

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
// 超能力模組">// 超能力模組
$superModule = new XPower;

// 初始化一個超人,並注入一個超能力模組依賴
$superMan = new Superman($superModule);

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
關於依賴注入這個本文的主要配角,也就這麼多需要講的。理解了依賴注入,我們就可以繼續深入問題。慢慢走近今天的主角……

<code class="javascript" data-origin=""
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
IoC 容器誕生的故事——科技時代(IoC容器)

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
剛剛列了一段代碼:

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
$superModule = new XPower;">$superModule = new XPower;

$superMan = new Superman($superModule);

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
讀者應該看出來了,手動的創建了一個超能力模組、手動的創建超人並注入了剛剛創建超能力模組。呵呵,手動

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
現代社會,應該是高效率的生產,乾淨的車間,完美的自動化裝配。

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
一羣怪獸來了,如此低效率產出超人是不現實,我們需要自動化 —— 最多一條指令,千軍萬馬來相見。我們需要一種高級的生產車間,我們只需要向生產車間提交一個腳本,工廠便能夠通過指令自動化生產。這種更爲高級的工廠,就是工廠模式的昇華 —— IoC 容器。

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
class Container">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);
    }
}

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
這時候,一個十分粗糙的容器就誕生了。現在的確很簡陋,但不妨礙我們進一步提升他。先着眼現在,看看這個容器如何使用吧!

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
// 創建一個容器(後面稱作超級工廠)">// 創建一個容器(後面稱作超級工廠)
$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');
// ...隨意添加

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
看到沒?通過最初的 綁定(bind) 操作,我們向 超級工廠 註冊了一些生產腳本,這些生產腳本在生產指令下達之時便會執行。發現沒有?我們徹底的解除了 超人 與 超能力模組 的依賴關係,更重要的是,容器類也絲毫沒有和他們產生任何依賴!我們通過註冊、綁定的方式向容器中添加一段可以被執行的回調(可以是匿名函數、非匿名函數、類的方法)作爲生產一個類的實例的 腳本 ,只有在真正的 生產(make) 操作被調用執行時,纔會觸發。

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
這樣一種方式,使得我們更容易在創建一個實例的同時解決其依賴關係,並且更加靈活。當有新的需求,只需另外綁定一個“生產腳本”即可。

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
實際上,真正的 IoC 容器更爲高級。我們現在的例子中,還是需要手動提供超人所需要的模組參數,但真正的 IoC 容器會根據類的依賴需求,自動在註冊、綁定的一堆實例中搜尋符合的依賴需求,並自動注入到構

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
現在,到目前爲止,我們已經不再懼怕怪獸們了。高智商人才集思廣益,井井有條,根據接口契約創造規範的超能力模組。超人開始批量產出。最終,人人都是超人,你也可以是哦 :stuck_out_tongue_closed_eyes:!

<code class="javascript" data-origin=""
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
laravel初始化一個服務容器的大概過程

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
對於laravel初始化服務容器的過程,還是以代碼加註釋的方式來大致的解釋一下,初始化過程都做了什麼工作
/public/index.php文件裏面有一行初始化服務器容器的代碼,調度的相關文件就是:/bootstrap/app.php

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
代碼清單/bootstrap/app.php

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
&lt;?php"><?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;

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
代碼清單Illuminate\Foundation\Application

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
//代碼太多,只能解釋幾個主要的方法(真實情況是,我瞭解也不多,也就看了這幾個方法*^_^*)">//代碼太多,只能解釋幾個主要的方法(真實情況是,我瞭解也不多,也就看了這幾個方法*^_^*)
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);
            }
        }
    }

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
由此得到的一個容器實例

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
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: []
}

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
怎麼打印一個實例??
到這一步爲止,你可以這樣做dd(app())

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
dd(app())什麼意思??
這裏包含兩個方法dd()和app(),具體定義請看自動加載的第四種方法

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
那說好的App::make(‘app’)方法咋不能用呢?
這是因爲這個方法需要用到Contracts,而到此爲止,還未定義App作爲Illuminate\Support\Facades\App的別名,因而不能用;需要等到統一入口文件裏面的運行Kernel類的handle方法才能用,所以在Controller裏面是可以用的,現在不能用

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
到此爲止,一個容器實例就誕生了,事情就是這麼個事情,情況就是這個個情況,再具體的那就需要你自己去看代碼了,我知道的就這些




<code class="javascript" data-origin=""
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
啓動Kernel代碼

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
Kernel實例調用handle方法,意味着laravel的核心和公用代碼已經準備完畢,此項目正式開始運行

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
代碼清單/app/Http/Kernel.php

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
&lt;?php namespace App\Http;"><?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',
    ];

}

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
大家看到了,其實這個文件裏面沒有handle方法,只有一些屬性定義,所以真正的handle方法,實在父類裏面實現的

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
代碼清單…/Illuminate/Foundation/Http/Kernel.php

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
//好多代碼,見幾個我看過的扯扯,其他的期待你們補上">//好多代碼,見幾個我看過的扯扯,其他的期待你們補上

//這個很重要,是項目的一些啓動引導項,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);
        };
    }

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
下面就需要根據URL和/app/Http/routes.php文件,進行Route操作

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
文件清單…/Illuminate/Routing/Router.php

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
//代碼好多,挑幾個解釋">//代碼好多,挑幾個解釋

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

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
文件清單…/Illuminate/Routing/ControllerDispatcher.php

<code class="javascript" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
    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);
                    });
    }

<code class="javascript" data-origin=""

<code class="php" data-origin="" 
<code class="php" data-origin="" 
<code class="php" data-origin="" 
這就是從入口文件到控制器中間,進行的一系列操作,心塞塞的,終於到我們幹活的地方了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章