RitoLabo

Laravelミドルウェアの基本入門(&出力HTMLをminifyしWebサイト高速化)

  • 公開:
  • 更新:
  • カテゴリ: PHP Laravel
  • タグ: PHP,Laravel,artisan,5.5,5.4,5.3,Middleware,5.6

Laravelが動作する一連の動作の中に「ミドルウェア」というメカニズムがあります。

HTTPリクエストに対してその入口と出口で動作し、認証からバリデーション、コンテンツ置換など様々な処理を行う事のできる。しかし一見表には出てこない影忍者のような万能メカニズムです。

Laravelのミドルウェアに関してはデフォルトでいくつかの処理が実装されていますが、自作することももちろんでき、しかもそれは想像次第で使い方を限定されない結構ミラクルなやつだったりします。まさに「使い方は、キミ次第。」というやつです。

という事で今回は、Laravelミドルウェアの入門的なフローの解説もしつつ、せっかくなのでWEBサイトの高速化を行ってみたいと思います。

アジェンダ
  1. 開発環境
  2. ミドルウェアについて
  3. ミドルウェアクラスの生成
  4. Before/Afterの指定
  5. ミドルウェア実装
  6. ミドルウェア登録
  7. ミドルウェアのアサイン
  8. 動作確認

開発環境

今回の開発環境については、以下の通りです。

  • Linux CentOS 7
  • Apache 2.4
  • PHP 7.2/7.1
  • Laravel

Laravelのバージョンについては5.6/5.5/5.4/5.3にて動作確認済みです。

Laravelのルートディレクトリを「laravel/」としています。

ミドルウェアについて

LaravelのミドルウェアにはBeforeミドルウェアAfterミドルウェアがあり、HTTPリクエストの入口と出口の2カ所で動作します。一連のHTTPリクエストとミドルウェア動作の流れは以下のようになっています。

Flow OF Laravel Middleware

エンドユーザの操作にて発生したHTTPリクエストは、ルーティングを通りコントローラに行く前にまずはBeforeミドルウェアによって処理が行われます。

必要な処理を行った上で問題がなければコントローラへ進み、その後、ビューを通りレンダリングが行われる直前で今度はAfterミドルウェアにて処理が行われます。

このようにして入口と出口で2回、ミドルウェアを通る事になりますが、作成したミドルウェアクラスに対して、入口と出口のどちらで発動するかを指定する事で動作を制御します。

Beforeミドルウェアに関しては認証系であったりなど、ある条件をクリアしていないリクエストに関しては弾く処理などを思い浮かべると想像もつきやすいかなと思います。

Afterミドルウェアに関しては、例えばどんな処理が思いつくでしょうか?Viewを通った後、つまりはBladeテンプレートへのバインドも済んだ後での処理になりますので、そこをどう使うかは本当にアイデア次第です。

なので今回はAfterミドルウェアを使いWEBサイトの高速化を行います。具体的には、レンダリング直前のHTMLソースをminifyし、転送量を減らす事での高速化を図りたいと思います。

ミドルウェアクラスの生成

まずは新たなミドルウェアクラスを生成します。Laravelルートディレクトリへ移動し、以下のartisanコマンドを叩きます。

# Laravelのルートディレクトリへ移動
cd /path/to/larvel

# artisanコマンドでミドルウェアファイルを生成する
php artisan make:middleware HtmlMinify

# 実行結果
[demo@localhost laravel]$ php artisan make:middleware HtmlMinify
Middleware created successfully.

laravel/app/Http/Middleware 配下に HtmlMinify.php が生成されます。

laravel
├─ app
│   ├─ Http
│   │   ├─ Middleware
│   │   │ ├─ EncryptCookies.php
│   │   │ ├─
HtmlMinify.php
│   │   │ ├─ RedirectIfAuthenticated.php
│   │   │ ├─ TrimStrings.php
│   │   │ └─ VerifyCsrfToken.php

初期ソースについては、handle()メソッドのみのシンプルな構成になっています。

laravel/app/Http/Middleware/HtmlMinify.php
<?php

namespace App\Http\Middleware;

use Closure;

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

Before/Afterの指定

このミドルウェアクラスがいつのタイミングで発動するかを制御するには、next()メソッドをどう扱うかによって制御できます。具体的には、以下のように扱う事で制御できます。

Beforeミドルウェアとして動作する
/**
* Beforeミドルウェアパターン
*/
public function handle($request, Closure $next)
{
/*
* ここに処理を実装する
*/

return $next($request);
}
Afterミドルウェアとして動作する
/**
* Afterミドルウェアパターン
*/
public function handle($request, Closure $next)
{
$response = $next($request);

/*
* ここに処理を実装する
*/

return $response;
}

ミドルウェア実装

それでは実際にミドルウェアクラスを定義してみたいと思います。

今回はAfterミドルウェアとして処理しますので、記述は以下のようになります。

laravel/app/Http/Middleware/HtmlMinify.php
<?php

namespace App\Http\Middleware;

use Closure;

class HtmlMinify
{
/**
* Handle an incoming request.
* 出力されるHTMLminifyする
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
// Afterミドルウェアで処理する
$response = $next($request);

// HTMLデータを取得
$content = $response->getContent();

$pattern = [
// 改行コードを削除
"/\n|\r|\r\n/",
// タグ間の不要空白を削除
"/(title|head|script|article|div|footer|h[1-5]|ul|ol|li|p|a|i|time|id=\".+?\"|class=\".+?\"|href=\".+?\"|datetime=\".+?\"|alt=\".+?\"|charset=\".+?\"|type=\".+?\")>\s*(.*?)\s*</",
];
$replace = [
"",
"$1>$2<",
];

// 置換
$content = preg_replace($pattern, $replace, $content);

// 置換したHTMLをセットする
$response->setContent($content);

return $response;
}
}

ぱっと眺めた感じ、処理自体はコンテンツの改行コードと空白などの除去しか行っていないのでそこは適宜行っていただくとして(ちなみにタグ間の不要空白を削除している部分は、全て除去しているわけではないのでそのまま使うと足りないか除去し過ぎる場合があるので、自身の環境に合わせて対象を抜き差ししてください)。

ポイントは以下のメソッドです。

// レンダリングされるHTMLデータを取得
$content = $response->getContent();

// 置換したHTMLをセットする
$response->setContent($content);

getContent()メソッドでレンダリングされる直前のHTMLデータを取得することが出来ます。これは、コントローラからビューへ渡された値をbladeテンプレートにバインディングした後の完成されたHTMLを取得できるので、ここに対して処理を行う事で、minifyが可能になっています。

そして、処理の終わったHTMLデータをsetContent()メソッドでレスポンスにセットしています。これで、処理後のHTMLデータがレンダリングされます。

ミドルウェア登録

ミドルウェアの実装が出来たらそれらを登録して、ミドルウェアとして動作させます。

Kernel.php を開いて、以下のように作成したミドルウェアを登録します。

laravel/app/Http/Kernel.php
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'html.minify' => \App\Http\Middleware\HtmlMinify::class, // ← 追加
];

識別子(プロパティ)「html.minify」の部分は、ミドルウェアを指定する際の名前になります。

ちなみに、Kernel.php には3つのプロパティがあります。それぞれの意味は以下の通りです。

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
protected $middleware = [];

protected $middlewareGroups = [
'web' => [],

'api' => [],

'aaa' => [],

'bbb' => [],
];

protected $routeMiddleware = [];
}
$middleware
アプリケーションのグローバルHTTPミドルウェアスタック。
ここに登録されたミドルウェアは、もれなく全てのタスクとして 実行されます。
$middlewareGroups
アプリケーションのルートミドルウェアグループ。
グループとしてミドルウェアを登録でき、ルートなどでそれらをまとめて指定できます。
$routeMiddleware
アプリケーションのルートミドルウェア。
ここに登録したミドルウェアは、グループに割り当てられるか、個別にてルートなどで指定し使用されます。

ミドルウェアのアサイン

最後にミドルウェアをアサインして実際に狙った部分で動作させます。

ルーティングに以下のように記述します。

// 単体で設定する場合
Route::get('sample', 'SampleController@index')->middleware('html.minify');

// まとめて設定する場合
Route::group(['middleware' => 'html.minify', 'prefix' => ''], function() {
Route::get('sample', 'SampleController@index');
Route::get('sample/aaa', 'SampleController@aaa');
Route::get('sample/bbb', 'SampleController@bbb');
});

単体でアサインすることもできますし、まとめてアサインすることもできます。

ちなみにミドルウェアはルーティングだけでなく、コントローラからでもアサインできたりします。

動作確認

それでは実際にブラウザでアクセスして動作を確認してみます。

ミドルウェアをかまさない場合

ミドルウェアをかまさない場合

ミドルウェアをかませた場合

ミドルウェアをかませた場合

問題なくminifyが行われました。

まとめ

以上で作業は完了です。

Laravelのミドルウェアはとても使い勝手が良く、気軽にHTTPリクエストの入口と出口に独自の機能・処理を立てる事が出来ます。ただしあまりやり過ぎると動きがもっさりしますので、重たい処理はキューへ投入するなどすると、ロギングなども快適にさばいてくれます。

アイデア次第で色々な使い方が出来るので、是非試してみてください。