laravel的啓動過程解析

若是沒有使用過相似Yii之類的框架,直接去看laravel,會有點一臉迷糊的感受,起碼我是這樣的。laravel的啓動過程,也是laravel的核心,對這個過程有一個瞭解,有助於駕輕就熟的使用框架,但願能對你們有點幫助。
提示:在此以前,最好看一下官方文檔,大概知道laravel,再來看這個筆記,這樣效果可能會好一點。javascript

統一入口

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

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



自動加載文件

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

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

代碼清單/bootstrap/autoload.phplaravel

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

代碼清單 laravel/vendor/autoload.phpredis

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

代碼清單laravel/vendor/composer/autoload_real.php:
主要是getLoader方法裏面,加了註釋的幾行,這是關鍵數據庫

<?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; } 
對於laravel自動加載過程的總結

laravel自動加載的過程就是這樣實現的,總結爲四種加載方式:編程

  1. PSR0加載方式—對應的文件就是autoload_namespaces.php
  2. PSR4加載方式—對應的文件就是autoload_psr4.php
  3. 其餘加載類的方式—對應的文件就是autoload_classmap.php
  4. 加載公用方法—對應的文件就是autoload_files.php
怎麼樣自定義自動加載方式

若是某些文件,須要自動自定義加載方式,能夠在Composer.json文件中定義json

"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"] }



服務容器——laravel真正的核心

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

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); /* $this->power = array( new Force(45), new Shot(99, 50, 2) ); */ } } 

咱們須要本身手動的在構造函數內(或者其餘方法裏)實例化一系列須要的類,這樣並很差。能夠想象,假如需求變動(不一樣的怪物橫行地球),須要更多的有針對性的 新的 超能力,或者須要 變動 超能力的方法,咱們必須 從新改造 超人。換句話說就是,改變超能力的同時,我還得從新制造個超人。效率過低了!新超人還沒創造完成世界早已被毀滅。

這時,靈機一動的人想到:爲何不能夠這樣呢?超人的能力能夠被隨時更換,只須要添加或者更新一個芯片或者其餘裝置啥的(想到鋼鐵俠沒)。這樣的話就不要整個從新來過了。

IoC 容器誕生的故事——青銅時代(工廠模式)

咱們不該該手動在 「超人」 類中固化了他的 「超能力」 初始化的行爲,而轉由外部負責,由外部創造超能力模組、裝置或者芯片等(咱們後面統一稱爲 「模組」),植入超人體內的某一個接口,這個接口是一個既定的,只要這個 「模組」 知足這個接口的裝置均可以被超人所利用,能夠提高、增長超人的某一種能力。這種由外部負責其依賴需求的行爲,咱們能夠稱其爲 「控制反轉(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]); // $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]) ); */ } } 

能夠看得出,咱們再也不須要在超人初始化之初,去初始化許多第三方類,只需初始化一個工廠類,便可知足需求。但這樣彷佛和之前區別不大,只是沒有那麼多 new 關鍵字。其實咱們稍微改造一下這個類,你就明白,工廠類的真正意義和價值了。

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

如今修改的結果使人滿意。如今,「超人」 的建立再也不依賴任何一個 「超能力」 的類,咱們如若修改了或者增長了新的超能力,只須要針對修改 SuperModuleFactory 便可。擴充超能力的同時再也不須要從新編輯超人的類文件,使得咱們變得很輕鬆。可是,這纔剛剛開始。

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]); // case 'more': ....... // case 'and more': ....... // case 'and more': ....... // case 'oh no! its too many!': ....... } } } 

看到沒。。。噩夢般的感覺!

其實靈感就差一步!你可能會想到更爲靈活的辦法!對,下一步就是咱們今天的主要配角 —— DI (依賴注入)

因爲對超能力模組的需求不斷增大,咱們須要集合整個世界的高智商人才,一塊兒解決問題,不該該僅僅只有幾個工廠壟斷負責。不太高智商人才們都很是自負,認爲本身的想法是對的,創造出的超能力模組沒有統一的接口,天然而然沒法被正常使用。這時咱們須要提出一種契約,這樣不管是誰創造出的模組,都符合這樣的接口,天然就可被正常使用。

interface SuperModuleInterface { /** * 超能力激活方法 * * 任何一個超能力都得有該方法,並擁有一個參數 *@param array $target 針對目標,能夠是一個或多個,本身或他人 */ public function activate(array $target); } 

上文中,咱們定下了一個接口 (超能力模組的規範、契約),全部被創造的模組必須遵照該規範,才能被生產。
其實,這就是 php 中 接口( interface ) 的用處和意義!不少人以爲,爲何 php 須要接口這種東西?難道不是 java 、 C# 之類的語言纔有的嗎?這麼說,只要是一個正常的面向對象編程語言(雖然 php 能夠面向過程),都應該具有這一特性。由於一個 對象(object) 自己是由他的模板或者原型 —— 類 (class) ,通過實例化後產生的一個具體事物,而有時候,實現統一種方法且不一樣功能(或特性)的時候,會存在不少的類(class),這時候就須要有一個契約,讓你們編寫出能夠被隨時替換卻不會產生影響的接口。這種由編程語言自己提出的硬性規範,會增長更多優秀的特性。

這時候,那些提出更好的超能力模組的高智商人才,遵循這個接口,建立了下述(模組)類:

/** * 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 } } 

改造完畢!如今,當咱們初始化 「超人」 類的時候,提供的模組實例必須是一個 SuperModuleInterface 接口的實現。不然就會提示錯誤。

正是因爲超人的創造變得容易,一個超人也就不須要太多的超能力,咱們能夠創造多個超人,並分別注入須要的超能力模組便可。這樣的話,雖然一個超人只有一個超能力,但超人更容易變多,咱們也不怕怪獸啦!

如今有人疑惑了,你要講的 依賴注入 呢?
其實,上面講的內容,正是依賴注入。

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

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

關於依賴注入這個本文的主要配角,也就這麼多須要講的。理解了依賴注入,咱們就能夠繼續深刻問題。慢慢走近今天的主角……

IoC 容器誕生的故事——科技時代(IoC容器)

剛剛列了一段代碼:

$superModule = new XPower; $superMan = new Superman($superModule); 

讀者應該看出來了,手動的建立了一個超能力模組、手動的建立超人並注入了剛剛建立超能力模組。呵呵,手動

現代社會,應該是高效率的生產,乾淨的車間,完美的自動化裝配。

一羣怪獸來了,如此低效率產出超人是不現實,咱們須要自動化 —— 最多一條指令,千軍萬馬來相見。咱們須要一種高級的生產車間,咱們只須要向生產車間提交一個腳本,工廠便可以經過指令自動化生產。這種更爲高級的工廠,就是工廠模式的昇華 —— 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'); // ...隨意添加 

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

這樣一種方式,使得咱們更容易在建立一個實例的同時解決其依賴關係,而且更加靈活。當有新的需求,只需另外綁定一個「生產腳本」便可。

實際上,真正的 IoC 容器更爲高級。咱們如今的例子中,仍是須要手動提供超人所須要的模組參數,但真正的 IoC 容器會根據類的依賴需求,自動在註冊、綁定的一堆實例中搜尋符合的依賴需求,並自動注入到構

如今,到目前爲止,咱們已經再也不害怕怪獸們了。高智商人才集思廣益,層次分明,根據接口契約創造規範的超能力模組。超人開始批量產出。最終,人人都是超人,你也能夠是哦 :stuck_out_tongue_closed_eyes:!

laravel初始化一個服務容器的大概過程

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

代碼清單/bootstrap/app.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; 

代碼清單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 ▼ #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

<?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操做

文件清單…/Illuminate/Routing/Router.php

//代碼好多,挑幾個解釋 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) { $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); }); }
相關文章
相關標籤/搜索