Laravel 5.1 源碼閱讀

安裝,和建立項目,都是經過Composer,簡單,略過。php

Entry && Kernel

網站入口文件,${Laravel-project}/public/index.PHP:laravel

$app = require_once __DIR__.'/../bootstrap/app.php';

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

$response->send();

$kernel->terminate($request, $response);

 

生成Request,處理Request(Http\Kernel::handle()),生成Response,發送Resonse。常規的Web處理流程。docker

注意 Illuminate\Contracts\Http\Kernel 只是一個Interface:bootstrap

interface Kernel
{
    public function bootstrap();
    public function handle($request);
    public function terminate($request, $response);
    public function getApplication();
}

 

可見入口文件中的代碼,$kernel = $app->make是關鍵,後面都是調用Kerenl接口的實例對象方法(特別是Kernel::handle())。數組

但是 $app 是誰建立的呢?是什麼類型呢?閉包

Bootstrap && Application

入口文件的第一行代碼:併發

$app = require_once __DIR__.'/../bootstrap/app.php';

 

引導咱們去查看 bootstrap/app.php 源碼,代碼很少,都拿過來看看吧:app

$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);

$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    App\Console\Kernel::class
);

$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);

return $app;

 

第一行建立,最後一行返回。如今咱們知道啦,$appIlluminate\Foundation\Application類型的對象。(由於require_once$app是一個單實例對象。中間幾行代碼後面可能有用,此處暫時忽略。)框架

天然,$kernel = $app->make()也就是調用Applicaton::make()了,代碼拿來看一下:ide

/**
 * Resolve the given type from the container.
 * (Overriding Container::make)
 * @return mixed
 */
public function make($abstract, array $parameters = [])
{
    $abstract = $this->getAlias($abstract);

    if (isset($this->deferredServices[$abstract])) {
        $this->loadDeferredProvider($abstract);
    }

    return parent::make($abstract, $parameters);
}

 

然而仍是不清楚它具體返回哪一個類型。

對照前面bootstrap的代碼:

$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);

 

咱們推論得出:$kernel = $app->make()的真實類型是App\Http\Kernel(同時實現了接口Illuminate\Contracts\Http\Kernel)。
這個推論很容易被證明,此處省略細節($app->singleton->bind->alias->make())。
更加具體的建立Kernel對象的細節十分瑣碎,咱們也一併忽略,有興趣的能夠去看父類方法Illuminate\Container\Container::make()/build()的代碼。

如今咱們初步總結一下:先new出Application類型對象app,app建立Kernel類型對象kernel,kernel處理Request、生成Response併發送給客戶端。

附加:$app是被直接new出來的(new Illuminate\Foundation\Application),其構造函數作了一些重要的初始化工做,整理代碼以下:

public function __construct($basePath = null)
{
    $this->instance('app', $this);
    $this->instance('Illuminate\Container\Container', $this);
    $this->register(new EventServiceProvider($this));
    $this->register(new RoutingServiceProvider($this));
    // ...
    $this->setBasePath($basePath);
}

 

Kernel::handle() && Request=>Response

下一步我想知道 $response = $kernel->handle($request) 內部具體作了什麼工做,是怎麼處理Request並生成Response的。

前面咱們已經分析過了,$kernel的真實類型是App\Http\Kernel,也實現了接口Illuminate\Contracts\Http\Kernel

拿來App\Http\Kernel::handle()的源代碼看看,咦,沒有此方法。
看其父類同名方法Illuminate\Foundation\Http\Kernel::handle()代碼:

public function handle($request)
{
    try {
        $request->enableHttpMethodParameterOverride();
        $response = $this->sendRequestThroughRouter($request);
    } catch (...) {
        // ...
    }
    $this->app['events']->fire('kernel.handled', [$request, $response]);
    return $response;
}

 

上面代碼中我感興趣的只有sendRequestThroughRouter($request)這個調用,進去看一下:

/**
 * Send the given request through the middleware / router.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return \Illuminate\Http\Response
 */
protected function sendRequestThroughRouter($request)
{
    $this->app->instance('request', $request);

    Facade::clearResolvedInstance('request');

    $this->bootstrap(); //! Note: $kernel->bootstrap();

    return (new Pipeline($this->app))
                ->send($request)
                ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                ->then($this->dispatchToRouter());
}

 

上面代碼中最後一行是咱們關注的重點,它把Request送進一個新建立的流水線(Pipeline),
供各個中間件(Middleware)處理,而後再派發給路由器(Router)。下文將展開分析。

Pipeline && Middleware && Router

流水線,中間件,路由器。

Pipleline

流水線Illuminate\Pipeline\Pipeline實現了接口Illuminate\Contracts\Pipeline\Pipeline
其主要接口方法有send,through,via,then。其中send設置Request對象,through設置中間件數組,via設置方法名(默認爲」handle」),then最終運行此並執行閉包參數(then的代碼極其複雜,各類閉包嵌套,把我搞糊塗了,有興趣的朋友能夠看一下)。

簡單推斷來講,其工做內容是:依次調用各中間件的handle方法。特別的,若是某個中間件是閉包,以閉包的形式調用之。

Middleware

中間件Illuminate\Contracts\Routing\Middleware是一個很簡單的接口:

interface Middleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next);
}

 

其文檔也極其簡陋,看不出太多有價值的信息。第二個參數什麼意思,返回值什麼意思,鬼才能看出來。可能須要從其餘地方入手研究中間件。

Router

將請求派發給Router的調用流程:$kernel->handle($request) => $kernel->sendRequestThroughRouter => $kernel->dispatchToRouter() => $kernel->router->dispatch($request)

其中$kernel->router是建立$kernel時經過構造函數傳入的Router對象。

有必要先看一下Router是怎樣建立出來的。調用流程:$app = new Applicaton(__construct) => $app->register(new RoutingServiceProvider($app)) => RoutingServiceProvider->register()->registerRouter()

protected function registerRouter()
{
    $this->app['router'] = $this->app->share(function ($app) {
        return new Router($app['events'], $app);
    });
}

 

Unresolved

流水線怎麼調用中間件,怎麼派發給路由器,路由器又是怎麼工做的呢?這中間有不少細節還沒搞明白。

流水線那裏,代碼很繞,暫時沒法理解。中間件那裏,文檔太簡陋,暫時沒法理解。路由器運行原理那裏,暫時尚未去看代碼。

目前就是這個樣子,此文到此爲止吧。我想我須要去看一下Laravel 5.1的基礎文檔,而後再回頭去讀源碼,可能效果會更好。

補記

Bootstrap

我以前在分析Kernel::handle()時,忽略了一個地方,Kernel::sendRequestThroughRouter()內部調用了Kernel::bootstrap()方法:

/**
 * Bootstrap the application for HTTP requests.
 *
 * @return void
 */
public function bootstrap()
{
    if (! $this->app->hasBeenBootstrapped()) {
        $this->app->bootstrapWith($this->bootstrappers());
    }
}

 

Kernel::bootstrap()內部又調用了Applicaton::bootstrapWith()

/**
 * Run the given array of bootstrap classes.
 *
 * @param  array  $bootstrappers
 * @return void
 */
public function bootstrapWith(array $bootstrappers)
{
    $this->hasBeenBootstrapped = true;

    foreach ($bootstrappers as $bootstrapper) {
        $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);

        $this->make($bootstrapper)->bootstrap($this);

        $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
    }
}

 

Applicaton::bootstrapWith()的參數是Kernel::bootstrappers(),其初始值爲:

/**
 * The bootstrap classes for the application.
 *
 * @var array
 */
protected $bootstrappers = [
    'Illuminate\Foundation\Bootstrap\DetectEnvironment',
    'Illuminate\Foundation\Bootstrap\LoadConfiguration',
    'Illuminate\Foundation\Bootstrap\ConfigureLogging',
    'Illuminate\Foundation\Bootstrap\HandleExceptions',
    'Illuminate\Foundation\Bootstrap\RegisterFacades',
    'Illuminate\Foundation\Bootstrap\RegisterProviders',
    'Illuminate\Foundation\Bootstrap\BootProviders',
];

以其中RegisterProviders爲例,其bootstrap()方法調用了$app->registerConfiguredProviders()

public function registerConfiguredProviders()
{
    $manifestPath = $this->getCachedServicesPath();

    (new ProviderRepository($this, new Filesystem, $manifestPath))
                ->load($this->config['app.providers']);
}

 

其中$this->config['app.providers']的值來自於文件config/app.php

'providers' => [

    /*
     * Laravel Framework Service Providers...
     */
    Illuminate\Foundation\Providers\ArtisanServiceProvider::class,
    Illuminate\Auth\AuthServiceProvider::class,
    Illuminate\Broadcasting\BroadcastServiceProvider::class,
    Illuminate\Bus\BusServiceProvider::class,
    Illuminate\Cache\CacheServiceProvider::class,
    Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
    Illuminate\Routing\ControllerServiceProvider::class,
    Illuminate\Cookie\CookieServiceProvider::class,
    Illuminate\Database\DatabaseServiceProvider::class,
    Illuminate\Encryption\EncryptionServiceProvider::class,
    Illuminate\Filesystem\FilesystemServiceProvider::class,
    Illuminate\Foundation\Providers\FoundationServiceProvider::class,
    Illuminate\Hashing\HashServiceProvider::class,
    Illuminate\Mail\MailServiceProvider::class,
    Illuminate\Pagination\PaginationServiceProvider::class,
    Illuminate\Pipeline\PipelineServiceProvider::class,
    Illuminate\Queue\QueueServiceProvider::class,
    Illuminate\Redis\RedisServiceProvider::class,
    Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
    Illuminate\Session\SessionServiceProvider::class,
    Illuminate\Translation\TranslationServiceProvider::class,
    Illuminate\Validation\ValidationServiceProvider::class,
    Illuminate\View\ViewServiceProvider::class,

    /*
     * Application Service Providers...
     */
    App\Providers\AppServiceProvider::class,
    App\Providers\AuthServiceProvider::class,
    App\Providers\EventServiceProvider::class,
    App\Providers\RouteServiceProvider::class,

],

你們都看到了,Kernel和Application互相交叉調用,Bootstrap過程又穿插在Request處理過程當中間。暫時看不出清晰的思路。

References

Laravel不是一個小項目,邏輯複雜,劃分模塊以後,佈局分散。你很難在短期內僅僅經過瀏覽源代碼理清框架主體設計思路,尤爲是在本人對Laravel還十分陌生的狀況下。適可而止是理智的選擇。

仍是先看一下基礎性的文檔吧:

相關文章
相關標籤/搜索