Laravel源碼解析--看看Lumen到底比Laravel輕在哪裏

在前面一篇《Laravel源碼解析--Laravel生命週期詳解》中我們利用xdebug詳細瞭解了下Laravel一次請求中到底做了哪些處理。今天我們跟 Lumen 對比下,看看 Lumen 比 Laravel 輕在哪裏?

1、Lumen生命週期

相比於Laravel,在Lumen中,你對框架有着更多的控制權。Lumen的入口文檔相比於Laravel要簡單許多。

<?php

/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| First we need to get an application instance. This creates an instance
| of the application / container and bootstraps the application so it
| is ready to receive HTTP / Console requests from the environment.
|
*/

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

/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request
| through the kernel, and send the associated response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have prepared for them.
|
*/

$app->run();

同樣的,我們先看下 bootstrap/app.php 文檔。

// 1、毫無疑問,首先加載自動加載
require_once __DIR__.'/../vendor/autoload.php';

// 2、將根目錄下的.env文檔中的配置加載到$_ENV、$_SERVER和putfile()中,對Laravel生命週期有印象的應該還記得
// Laravel中是在$bootstrappers數組中,先加載.ENV文檔,然後再加載config/*.php裏的所有配置文檔。
// 而在Lumen中,默認只加載了.ENV中的文檔,你可以將所有的配置信息都寫在.ENV文檔中,然後在項目中可以用env()方法獲取。
// 當然,你也可以像Laravel那樣,將所有的配置信息放到config/*.php下面,不過這需要你在獲取$app實例後手動調用$app->configure('api');方法。
// 這裏就是Lumen比Laravel輕的一個地方,但我感覺只使用.ENV文檔或config配置都不太合理,還是兩個配合使用最為方便。所以這裏Lumen就不會比Laravel輕了。
try {
    (new Dotenv\Dotenv(dirname(__DIR__)))->load();
} catch (Dotenv\Exception\InvalidPathException $e) {
    //
}

/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| Here we will load the environment and create the application instance
| that serves as the central piece of this framework. We'll use this
| application as an "IoC" container and router for this framework.
|
*/
// 3、創建Lumen App實例
$app = new Laravel\Lumen\Application(
    dirname(__DIR__)
);

創建Lumen實例的時候,肯定也做了一些初始化工作。

/**
     * Create a new Lumen application instance.
     *
     * @param  string|null  $basePath
     * @return void
     */
    public function __construct($basePath = null)
    {
     // 設置時區
if (! empty(env('APP_TIMEZONE'))) { date_default_timezone_set(env('APP_TIMEZONE', 'UTC')); }      // 設置路徑 $this->basePath = $basePath;
    // 向容器中的instances中綁定app,path,env,config等,並綁定一些aliases
$this->bootstrapContainer();
     // 註冊錯誤處理
$this->registerErrorHandling();
     // 向容器中注入Router
$this->bootstrapRouter(); }

相比Laravel,Lumen做的初始化工作要少很多--包自動發現、路徑的註冊、一些基礎服務提供者的註冊、註冊aliases時也少了很多。得到App實例後,讓我們繼續回頭看 bootstraps/app.php 中去。

// 3、創建Lumen App實例
$app = new Laravel\Lumen\Application(
    dirname(__DIR__)
);

// 4、如果你需要在應用中使用門面方法,則取消此行註釋
// $app->withFacades();

// 5、如果你想要在應用中使用Eloquent,則取消此行註釋
// $app->withEloquent();

/*
|--------------------------------------------------------------------------
| Register Container Bindings
|--------------------------------------------------------------------------
|
| Now we will register a few bindings in the service container. We will
| register the exception handler and the console kernel. You may add
| your own bindings here if you like or you can make another file.
|
*/
// 6、註冊容器綁定,綁定異常處理類的實現和Console的實現。
// 相比Laravel,Lumen少綁定了一個Http\Kernel的實現
$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);

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

/*
|--------------------------------------------------------------------------
| Register Middleware
|--------------------------------------------------------------------------
|
| Next, we will register the middleware with the application. These can
| be global middleware that run before and after each request into a
| route or middleware that'll be assigned to some specific routes.
|
*/

// 7、每個request都需要通過的一箇中間件組,相比Laravel,少了一些固定要走的中間件,那麼如果你把Laravel中固定的中間件註釋掉,是不是就一樣了呢?
// $app->middleware([
//     App\Http\Middleware\ExampleMiddleware::class
// ]);

// 8、註冊路由中間件,同樣在Laravel中,也默認定義了很多路由中間件
// $app->routeMiddleware([
//     'auth' => App\Http\Middleware\Authenticate::class,
// ]);

/*
|--------------------------------------------------------------------------
| Register Service Providers
|--------------------------------------------------------------------------
|
| Here we will register all of the application's service providers which
| are used to bind services into the container. Service providers are
| totally optional, so you are not required to uncomment this line.
|
*/

// 9、按需註冊服務提供者,而在Laravel中,除了註冊了幾個基礎的服務提供在,還會註冊config/app.php中providers數組裏所有的服務提供者。
// 還是那句話,如果你把config/app.php中providers數組中都註釋掉呢?對了,在Laravel5.5引入包自動發現後,它還會註冊bootstrap/cache/packages.php中的服務提供者
 $app->register(App\Providers\AppServiceProvider::class);
 $app->register(App\Providers\AuthServiceProvider::class);
 $app->register(App\Providers\EventServiceProvider::class);

/*
|--------------------------------------------------------------------------
| Load The Application Routes
|--------------------------------------------------------------------------
|
| Next we will include the routes file so that they can all be added to
| the application. This will provide all of the URLs the application
| can respond to, as well as the controllers that may handle them.
|
*/
// 10、註冊路由
$app->router->group([
    'namespace' => 'App\Http\Controllers',
], function ($router) {
    require __DIR__.'/../routes/web.php';
});

// 11、返回App實例
return $app;

到這裏,整個Lumen應用已經初始化完畢。之後 $app->run(); 其實就是根據之前註冊的路由(執行每個服務提供者的boot()方法,也在這個方法中),然後進行路由分發。這與Laravel並沒有什麼太大區別。

綜上所述,我們總結一下Lumen應用比Laravel在哪些方面輕了。

  1. 關於.ENV文檔和config配置文檔的加載,但實際上更建議將Lumen的與Laravel的保持一致
  2. 在new App實例的時候,Lumen所做的初始化工作,比Laravel少了一些,例如,包自動發現,路徑的註冊,基本服務提供者和一些aliases等。
  3. 關於request需要通過默認中間件。但其實我覺得有些還是必要的,比如\App\Http\Middleware\CheckForMaintenanceMode::class, 和\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, 等。
  4. 我個人認為應該是相比與Laravel,耗時最多的一個,就是對各種服務提供者的註冊和引導(執行boot()方法)。

下面做個測試,當不對Laravel做任何處理的時候,看看啟動需要多久?

Route::get('/', function () {
    $end = microtime(true);
    return $end - LARAVEL_START;
//    return view('welcome');
});

差不多是在 0.16353797912598 秒左右。而Lumen在開啟一些基本的服務後的啟動時間為 0.043106079101562 ,與Laravel相差了將近有4倍。相當於光啟動框架,任何邏輯處理都還沒開始,Laravel比Lumen多耗時0.12s。如果我們利用一些Laravel的性能優化策略,比如緩存配置文檔等會稍稍減少Laravel的啟動時間 0.12118005752563 。因為php的特性,每一個request,都需要將之前的所有動作執行一遍,所以App的啟動還是一個比較耗時的任務。

這時候我就想安利一下顛覆php的拓展--swoole。下一章會講解給Laravel插上翅膀的一個用swoole做的composer包。

關鍵詞:laravel app the lumen application 註冊 class env we this

相關推薦:

Laravel源碼分析--Laravel生命週期詳解

淺談Laravel中的設計模式(三) Container 容器

Laravel 5.6 Server Side Validation Example Using Resource Controller

Events and Listeners Example Using Laravel 5.6

一個 GitHub 上的 Laravel 以太坊擴展包 —— Laravel-ethereum

laravel中使用的PDF擴展包——laravel-dompdf和laravel-snappy

[Laravel] 14 - REST API: Laravel from scratch

laravel實現支付寶支付功能

使用 Swoole 來加速你的 Laravel 應用