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

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

  • 公開日
  • 更新日
  • カテゴリ:Laravel
  • タグ:PHP,Laravel,artisan,Middleware
Laravelミドルウェアの基本入門(&出力HTMLをminifyしWebサイト高速化)

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

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

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

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

Contents

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

開発環境

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

  • 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 リクエストとミドルウェア動作の流れは以下のようになっています。

エンドユーザの操作にて発生した 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.
     * 出力されるHTMLをminifyする
     *
     * @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 リクエストの入口と出口に独自の機能・処理を立てる事が出来ます。ただしあまりやり過ぎると動きがもっさりしますので、重たい処理はキューへ投入するなどすると、ロギングなども快適にさばいてくれます。

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

Author

rito

  • Backend Engineer
  • Tokyo, Japan
  • PHP 5 技術者認定上級試験 認定者
  • 統計検定 3 級