Laravel 7.x HTTPセッション

イントロダクション

HTTP駆動のアプリケーションはステートレスのため、リクエスト間に渡りユーザーに関する情報を保存するセッションが提供されています。Laravelは記述的で統一されたAPIを使えるさまざまなバックエンドのセッションを用意しています。人気のあるMemcachedRedis、データベースも始めからサポートしています。

設定

セッションの設定はconfig/session.phpにあります。このファイルのオプションには詳しくコメントがついていますので確認してください。ほとんどのアプリケーションでうまく動作できるように、Laravelはfileセッションドライバをデフォルトとして設定しています。

セッションドライバ(driver)はリクエスト毎のセッションデータをどこに保存するかを決めます。Laravelには最初から素晴らしいドライバが用意されています。

  • file - セッションはstorage/framework/sessionsに保存されます。
  • cookie - セッションは暗号化され安全なクッキーに保存されます。
  • database - セッションはリレーショナルデータベースへ保存されます。
  • memcachedredis - セッションはスピードの早いキャッシュベースの保存域に保存されます。
  • array - セッションはPHPの配列として保存されるだけで、リクエスト間で継続しません。

Tip!! セッションデータを持続させないため、arrayドライバは通常テスト時に使用します。

ドライバの事前要件

データベース

databaseセッションドライバを使う場合、セッションアイテムを含むテーブルを作成する必要があります。以下にこのテーブル宣言のサンプル「スキーマ」を示します。

Schema::create('sessions', function ($table) {
    $table->string('id')->unique();
    $table->foreignId('user_id')->nullable();
    $table->string('ip_address', 45)->nullable();
    $table->text('user_agent')->nullable();
    $table->text('payload');
    $table->integer('last_activity');
});

session:table Artisanコマンドを使えば、このマイグレーションが生成できます。

php artisan session:table

php artisan migrate

Redis

RedisセッションをLaravelで使用する前に、PECLによりPhpRedis PHP拡張、もしくはComposerでpredis/predisパッケージ(~1.0)をインストールする必要があります。Redis設定の詳細は、Laravelのドキュメントをご覧ください。

Tip!! session設定ファイルでは、connectionオプションで、どのRedis接続をセッションで使用するか指定します。

セッションの使用

データ取得

Laravelでセッションを操作するには、主に2つの方法があります。グローバルなsessionヘルパを使用する方法と、コントローラメソッドにタイプヒントで指定できるRequestインスタンスを経由する方法です。最初はRequestインスタンスを経由する方法を見てみましょう。コントローラのメソッドに指定した依存インスタンスは、Laravelのサービスコンテナにより、自動的に注入されることを覚えておきましょう。

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class UserController extends Controller
{
    /**
     * 指定されたユーザーのプロフィールを表示
     *
     * @param  Request  $request
     * @param  int  $id
     * @return Response
     */
    public function show(Request $request, $id)
    {
        $value = $request->session()->get('key');

        //
    }
}

getメソッドでセッションから値を取り出すとき、第2引数にデフォルト値も指定できます。このデフォルト値は、セッションに指定したキーが存在していなかった場合に、返されます。getメソッドのデフォルト値に「クロージャ」を渡した場合に、要求したキーが存在しなければ、その「クロージャ」が実行され、結果が返されます。

$value = $request->session()->get('key', 'default');

$value = $request->session()->get('key', function () {
    return 'default';
});

sessionグローバルヘルパ

グローバルなsession PHP関数で、セッションからデータを出し入れすることもできます。sessionヘルパが文字列ひとつだけで呼び出されると、そのセッションキーに対する値を返します。ヘルパがキー/値ペアの配列で呼び出されると、それらの値はセッションへ保存されます。

Route::get('home', function () {
    // セッションから一つのデータを取得する
    $value = session('key');

    // デフォルト値を指定する場合
    $value = session('key', 'default');

    // セッションへ一つのデータを保存する
    session(['key' => 'value']);
});

Tip!! セッションをHTTPリクエストインスタンスを経由する場合と、グローバルなsessionヘルパを使用する場合では、実践上の違いがあります。どんなテストケースであろうとも使用可能な、assertSessionHasメソッドを利用して、どちらの手法もテスト可能です。

全セッションデータの取得

セッション中の全データを取得する場合は、allメソッドを使います。

$data = $request->session()->all();

セッション中のアイテム存在を確認

セッションへ値が存在するか調べたい場合は、hasメソッドを使います。その値が存在し、nullでない場合はtrueが返ります。

if ($request->session()->has('users')) {
    //
}

セッション中に、たとえ値がnullであろうとも存在していることを確認したい場合は、existsメソッドを使います。existsメソッドは、値が存在していればtrueを返します。

if ($request->session()->exists('users')) {
    //
}

データ保存

セッションへデータを保存する場合、通常putメソッドか、sessionヘルパを使用します。

// リクエストインスタンス経由
$request->session()->put('key', 'value');

// グローバルヘルパ使用
session(['key' => 'value']);

配列セッション値の追加

pushメソッドは新しい値を配列のセッション値へ追加します。たとえばuser.teamsキーにチーム名の配列が含まれているなら、新しい値を次のように追加できます。

$request->session()->push('user.teams', 'developers');

取得後アイテムを削除

pullメソッド一つで、セッションからアイテムを取得後、削除できます。

$value = $request->session()->pull('key', 'default');

フラッシュデータ

次のリクエスト間だけセッションにアイテムを保存したいことがあります。flashメソッドを使ってください。flashメソッドは現在と直後のHTTPリクエストの間だけ、セッションにデータを保存し、それ以降は削除します。フラッシュデータは主にステータスメッセージなど、持続しない情報に便利です。

$request->session()->flash('status', 'Task was successful!');

フラッシュデータをその先のリクエストまで持続させたい場合は、reflashメソッドを使い、全フラッシュデータを次のリクエストまで持続させられます。特定のフラッシュデータのみ持続させたい場合は、keepメソッドを使います。

$request->session()->reflash();

$request->session()->keep(['username', 'email']);

データ削除

forgetメソッドでセッションからデータを削除できます。セッションから全データを削除したければ、flushメソッドが使用できます。

// 1キーを削除
$request->session()->forget('key');

// 複数キーを削除
$request->session()->forget(['key1', 'key2']);

$request->session()->flush();

セッションIDの再生成

セッションIDの再生成は多くの場合、悪意のあるユーザーからの、アプリケーションに対するsession fixation攻撃を防ぐために行います。

Laravelに組み込まれているLoginControllerを使用していれば、認証中にセッションIDは自動的に再生成されます。しかし、セッションIDを任意に再生成する必要があるのでしたら、regenerateメソッドを使ってください。

$request->session()->regenerate();

セッションのブロック

Note: セッションのブロックを使用するには、アトミックロックをサポートするキャッシュドライバを使用する必用があります。現在、memcacheddynamodbredisdatabaseのキャッシュドライバがサポートしています。さらに、cookieセッションドライバは使用できません。

Laravelはデフォルトで同じセッションを使用するリクエストを同時に実行できます。そのため、たとえばJavaScript HTTPライブラリを使用してアプリケーションに2つのHTTPリクエストを送信すると、両方が同時に実行されます。多くのアプリケーションでは、これは問題ではありません。ただし、セッションデータの損失は、両方がセッションにデータを書き込む2つの異なるアプリケーションエンドポイントに、同時要求を行うアプリケーションの小さなサブセットで発生する可能性があります。

これを軽減するために、Laravelでは特定のセッションの同時リクエストを制限できる機能を提供します。開始するには、blockメソッドをルート定義にチェーンするだけです。 この例では、/profileエンドポイントの受信リクエストがセッションロックを取得します。このロックが保持されている間、同じセッションIDを共有する/profileまたは/orderエンドポイントへの受信リクエストは、最初のリクエストの実行が完了するのを待ってから、実行を続行します。

Route::post('/profile', function () {
    //
})->block($lockSeconds = 10, $waitSeconds = 10)

Route::post('/order', function () {
    //
})->block($lockSeconds = 10, $waitSeconds = 10)

blockメソッドは2つのオプションの引数を取ります。blockメソッドの最初の引数は、セッションロックを解放する前に保持する最大秒数です。もちろん、リクエストがこの時間より前に実行終了した場合、ロックをより早く解放します。

blockメソッドの2番目の引数は、セッションロックを取得するときにリクエストが待つ秒数です。リクエストが指定秒数内にセッションロックを取得できない場合、Illuminate\Contracts\Cache\LockTimoutExceptionが投げられます。

両引数のどちらも渡さない場合、ロックを最大10秒間取得し、リクエストはロックを取得するまで最大10秒間待ちます。

Route::post('/profile', function () {
    //
})->block()

カスタムセッションドライバの追加

ドライバの実装

カスタムセッションドライバでは、SessionHandlerInterfaceを実装してください。このインターフェイスには実装する必要のある、シンプルなメソッドが数個含まれています。MongoDBの実装をスタブしてみると、次のようになります。

<?php

namespace App\Extensions;

class MongoSessionHandler implements \SessionHandlerInterface
{
    public function open($savePath, $sessionName) {}
    public function close() {}
    public function read($sessionId) {}
    public function write($sessionId, $data) {}
    public function destroy($sessionId) {}
    public function gc($lifetime) {}
}

Tip!! こうした拡張を含むディレクトリをLaravelでは用意していません。お好きな場所に設置してください。上記の例では、Extensionディレクトリを作成し、MongoSessionHandlerファイルを設置しています。

これらのメソッドの目的を読んだだけでは理解しづらいため、それぞれのメソッドを簡単に見てみましょう。

  • openメソッドは通常ファイルベースのセッション保存システムで使われます。Laravelはfileセッションドライバを用意していますが、皆さんはこのメソッドに何も入れる必要はないでしょう。空のスタブのままで良いでしょう。実際、PHPが実装するように要求しているこのメソッドは、下手なインターフェイスデザインなのです。
  • closeメソッドもopenと同様に通常は無視できます。ほどんどのドライバでは必要ありません。
  • readメソッドは指定された$sessionIdと紐付いたセッションデータの文字列バージョンを返します。取得や保存時にドライバ中でデータをシリアライズしたり、他のエンコード作業を行ったりする必要はありません。Laravelがシリアライズを行います。
  • writeメソッドはMongoDBやDynamoなどの持続可能なストレージに、$sessionIdに紐付け指定した$data文字列を書き出します。ここでも、シリアリズを行う必要はまったくありません。Laravelがすでに処理しています。
  • destroyメソッドは持続可能なストレージから$sessionIdに紐付いたデータを取り除きます。
  • gcメソッドは指定したUNIXタイムスタンプの$lifetimeよりも古い前セッションデータを削除します。自前で破棄するMemcachedやRedisのようなシステムでは、このメソッドは空のままにしておきます。

ドライバの登録

ドライバを実装したら、フレームワークへ登録する準備が整いました。Laravelのセッションバックエンドへドライバを追加するには、Sessionファサードextendメソッドを呼び出します。サービスプロバイダbootメソッドから、extendメソッドを呼び出してください。既存のAppServiceProviderか真新しく作成し、呼び出してください。

<?php

namespace App\Providers;

use App\Extensions\MongoSessionHandler;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\ServiceProvider;

class SessionServiceProvider extends ServiceProvider
{
    /**
     * 全アプリケーションサービスの登録
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * 全アプリケーションサービスの初期起動
     *
     * @return void
     */
    public function boot()
    {
        Session::extend('mongo', function ($app) {
            // Return implementation of SessionHandlerInterface...
            return new MongoSessionHandler;
        });
    }
}

セッションドライバを登録したら、config/session.php設定ファイルでmongoドライバが使用できます。

ドキュメント章別ページ

ヘッダー項目移動

注目:アイコン:ページ内リンク設置(リンクがないヘッダーへの移動では、リンクがある以前のヘッダーのハッシュをURLへ付加します。

移動

クリックで即時移動します。

設定

適用ボタンクリック後に、全項目まとめて適用されます。

カラーテーマ
和文指定 Pagination
和文指定 Scaffold
Largeスクリーン表示幅
インデント
本文フォント
コードフォント
フォント適用確認

フォントの指定フィールドから、フォーカスが外れると、当ブロックの内容に反映されます。EnglishのDisplayもPreviewしてください。

フォント設定時、表示に不具合が出た場合、当サイトのクッキーを削除してください。

バックスラッシュを含むインライン\Code\Blockの例です。

以下はコードブロックの例です。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザに関連する電話レコードを取得
     */
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

設定を保存する前に、表示が乱れないか必ず確認してください。CSSによるフォントファミリー指定の知識がない場合は、フォントを変更しないほうが良いでしょう。

キーボード・ショートカット

オープン操作

PDC

ページ(章)移動の左オフキャンバスオープン

HA

ヘッダー移動モーダルオープン

MS

移動/設定の右オフキャンバスオープン

ヘッダー移動

T

最初のヘッダーへ移動

E

最後のヘッダーへ移動

NJ

次ヘッダー(H2〜H4)へ移動

BK

前ヘッダー(H2〜H4)へ移動

その他

?

このヘルプページ表示
閉じる