HTTPセッション
イントロダクション
HTTP駆動のアプリケーションはステートレスであるため、セッションは複数のリクエストにわたってユーザーに関する情報を保存する方法を提供します。そのユーザー情報は通常、後続のリクエストからアクセスできる永続的なストア/バックエンドに配置されます。
Laravelには、表現力豊かで統一されたAPIを介してアクセスされるさまざまなセッションバックエンドが同梱されています。Memcached、Redis、データベースなどの一般的なバックエンドのサポートが含まれています。
設定
アプリケーションのセッション設定ファイルは config/session.php に保存されています。このファイルで利用可能なオプションを確認してください。デフォルトで、Laravelは database セッションドライバを使用するように設定されています。
セッションの driver 設定オプションは、各リクエストのセッションデータがどこに保存されるかを定義します。Laravelにはさまざまなドライバが含まれています
file- セッションはstorage/framework/sessionsに保存されます。cookie- セッションは安全な暗号化されたクッキーに保存されます。database- セッションはリレーショナルデータベースに保存されます。memcached/redis- セッションはこれらの高速なキャッシュベースのストアのいずれかに保存されます。dynamodb- セッションはAWS DynamoDBに保存されます。array- セッションはPHP配列に保存され、永続化されません。
arrayドライバは主にテスト中に使用され、セッションに保存されたデータが永続化されるのを防ぎます。
ドライバの前提条件
データベース
database セッションドライバを使用する場合、セッションデータを格納するデータベーステーブルがあることを確認する必要があります。通常、これはLaravelのデフォルトの 0001_01_01_000000_create_users_table.php データベースマイグレーションに含まれています。しかし、何らかの理由で sessions テーブルがない場合は、make:session-table Artisanコマンドを使用してこのマイグレーションを生成できます
1php artisan make:session-table2 3php artisan migrate
Redis
LaravelでRedisセッションを使用する前に、PECLを介してPhpRedis PHP拡張機能をインストールするか、Composerを介して predis/predis パッケージ(~1.0)をインストールする必要があります。Redisの設定に関する詳細は、LaravelのRedisドキュメントを参照してください。
SESSION_CONNECTION 環境変数、または session.php 設定ファイルの connection オプションを使用して、セッションストレージに使用されるRedis接続を指定できます。
セッションの操作
データの取得
Laravelでセッションデータを操作する主な方法は2つあります。グローバルな session ヘルパーと Request インスタンスを介する方法です。まず、ルートクロージャやコントローラメソッドでタイプヒントできる Request インスタンスを介してセッションにアクセスする方法を見てみましょう。コントローラメソッドの依存関係は、Laravelのサービスコンテナを介して自動的に注入されることを覚えておいてください。
1<?php 2 3namespace App\Http\Controllers; 4 5use Illuminate\Http\Request; 6use Illuminate\View\View; 7 8class UserController extends Controller 9{10 /**11 * Show the profile for the given user.12 */13 public function show(Request $request, string $id): View14 {15 $value = $request->session()->get('key');16 17 // ...18 19 $user = $this->users->find($id);20 21 return view('user.profile', ['user' => $user]);22 }23}
セッションからアイテムを取得するとき、get メソッドの2番目の引数としてデフォルト値を渡すこともできます。このデフォルト値は、指定されたキーがセッションに存在しない場合に返されます。get メソッドにデフォルト値としてクロージャを渡し、要求されたキーが存在しない場合、クロージャが実行され、その結果が返されます。
1$value = $request->session()->get('key', 'default');2 3$value = $request->session()->get('key', function () {4 return 'default';5});
グローバルセッションヘルパー
グローバルな session PHP関数を使用して、セッション内のデータを取得および保存することもできます。session ヘルパーが単一の文字列引数で呼び出されると、そのセッションキーの値を返します。ヘルパーがキー/値のペアの配列で呼び出されると、それらの値がセッションに保存されます。
1Route::get('/home', function () { 2 // Retrieve a piece of data from the session... 3 $value = session('key'); 4 5 // Specifying a default value... 6 $value = session('key', 'default'); 7 8 // Store a piece of data in the session... 9 session(['key' => 'value']);10});
HTTPリクエストインスタンスを介してセッションを使用する方法と、グローバルな session ヘルパーを使用する方法の間に実用的な違いはほとんどありません。どちらの方法も、すべてのテストケースで利用可能な assertSessionHas メソッドを介してテスト可能です。
すべてのセッションデータの取得
セッション内のすべてのデータを取得したい場合は、all メソッドを使用できます。
1$data = $request->session()->all();
セッションデータの一部を取得
only および except メソッドを使用して、セッションデータのサブセットを取得できます。
1$data = $request->session()->only(['username', 'email']);2 3$data = $request->session()->except(['username', 'email']);
アイテムがセッションに存在するかどうかの判断
セッションにアイテムが存在するかどうかを判断するには、has メソッドを使用できます。has メソッドは、アイテムが存在し、かつ null でない場合に true を返します。
1if ($request->session()->has('users')) {2 // ...3}
アイテムの値が null であってもセッションに存在するかどうかを判断するには、exists メソッドを使用できます。
1if ($request->session()->exists('users')) {2 // ...3}
セッションにアイテムが存在しないかどうかを判断するには、missing メソッドを使用できます。missing メソッドは、アイテムが存在しない場合に true を返します。
1if ($request->session()->missing('users')) {2 // ...3}
データの保存
セッションにデータを保存するには、通常、リクエストインスタンスの put メソッドまたはグローバルな session ヘルパーを使用します。
1// Via a request instance...2$request->session()->put('key', 'value');3 4// Via the global "session" helper...5session(['key' => 'value']);
配列セッション値へのプッシュ
push メソッドを使用して、配列であるセッション値に新しい値を追加できます。例えば、user.teams キーにチーム名の配列が含まれている場合、次のように新しい値を配列に追加できます。
1$request->session()->push('user.teams', 'developers');
アイテムの取得と削除
pull メソッドは、セッションからアイテムを取得して削除するのを1つのステートメントで行います。
1$value = $request->session()->pull('key', 'default');
セッション値のインクリメントとデクリメント
セッションデータにインクリメントまたはデクリメントしたい整数が含まれている場合、increment および decrement メソッドを使用できます。
1$request->session()->increment('count');2 3$request->session()->increment('count', $incrementBy = 2);4 5$request->session()->decrement('count');6 7$request->session()->decrement('count', $decrementBy = 2);
フラッシュデータ
次のリクエストのためにセッションにアイテムを保存したい場合があります。その場合は flash メソッドを使用できます。このメソッドを使用してセッションに保存されたデータは、すぐに利用可能になり、後続のHTTPリクエスト中でも利用できます。後続のHTTPリクエストの後、フラッシュされたデータは削除されます。フラッシュデータは、主に短命なステータスメッセージに役立ちます。
1$request->session()->flash('status', 'Task was successful!');
フラッシュデータを複数のリクエストにわたって永続させる必要がある場合は、reflash メソッドを使用できます。これにより、すべてのフラッシュデータが追加のリクエストのために保持されます。特定のフラッシュデータのみを保持する必要がある場合は、keep メソッドを使用できます。
1$request->session()->reflash();2 3$request->session()->keep(['username', 'email']);
現在のリクエストのみでフラッシュデータを永続させるには、now メソッドを使用できます。
1$request->session()->now('status', 'Task was successful!');
データの削除
forget メソッドは、セッションからデータの一部を削除します。セッションからすべてのデータを削除したい場合は、flush メソッドを使用できます。
1// Forget a single key...2$request->session()->forget('name');3 4// Forget multiple keys...5$request->session()->forget(['name', 'status']);6 7$request->session()->flush();
セッションIDの再生成
セッションIDの再生成は、悪意のあるユーザーがアプリケーションに対するセッション固定攻撃を悪用するのを防ぐためにしばしば行われます。
Laravel アプリケーションスターターキットのいずれか、または Laravel Fortify を使用している場合、Laravelは認証中にセッションIDを自動的に再生成します。ただし、手動でセッションIDを再生成する必要がある場合は、regenerate メソッドを使用できます。
1$request->session()->regenerate();
セッションIDを再生成し、セッションからすべてのデータを1つのステートメントで削除する必要がある場合は、invalidate メソッドを使用できます。
1$request->session()->invalidate();
セッションブロッキング
セッションブロッキングを利用するには、アプリケーションがアトミックロックをサポートするキャッシュドライバを使用している必要があります。現在、それらのキャッシュドライバには memcached、dynamodb、redis、mongodb(公式の mongodb/laravel-mongodb パッケージに含まれる)、database、file、および array ドライバが含まれます。さらに、cookie セッションドライバは使用できません。
デフォルトでは、Laravelは同じセッションを使用するリクエストを同時に実行することを許可します。したがって、例えば、JavaScript HTTPライブラリを使用してアプリケーションに2つのHTTPリクエストを行うと、それらは両方とも同時に実行されます。多くのアプリケーションでは、これは問題ありません。しかし、セッションにデータを書き込む2つの異なるアプリケーションエンドポイントに同時にリクエストを行う一部のアプリケーションでは、セッションデータの損失が発生する可能性があります。
これを軽減するために、Laravelは特定のセッションに対する同時リクエストを制限する機能を提供します。開始するには、ルート定義に block メソッドをチェーンするだけです。この例では、/profile エンドポイントへの受信リクエストはセッションロックを取得します。このロックが保持されている間、同じセッションIDを共有する /profile または /order エンドポイントへの受信リクエストは、最初のリクエストが実行を完了するまで待機し、その後実行を続行します。
1Route::post('/profile', function () {2 // ...3})->block($lockSeconds = 10, $waitSeconds = 10);4 5Route::post('/order', function () {6 // ...7})->block($lockSeconds = 10, $waitSeconds = 10);
block メソッドは2つのオプションの引数を受け入れます。block メソッドが受け入れる最初の引数は、セッションロックが解放される前に保持されるべき最大秒数です。もちろん、リクエストがこの時間より前に実行を完了した場合、ロックはより早く解放されます。
block メソッドが受け入れる2番目の引数は、リクエストがセッションロックを取得しようとしている間に待機するべき秒数です。リクエストが指定された秒数内にセッションロックを取得できない場合、Illuminate\Contracts\Cache\LockTimeoutException がスローされます。
これらの引数のいずれも渡されない場合、ロックは最大10秒間取得され、リクエストはロックを取得しようとしている間、最大10秒間待機します。
1Route::post('/profile', function () {2 // ...3})->block();
カスタムセッションドライバの追加
ドライバの実装
既存のセッションドライバのいずれもアプリケーションのニーズに合わない場合、Laravelは独自のセッションハンドラを作成することを可能にします。カスタムセッションドライバは、PHPの組み込み SessionHandlerInterface を実装する必要があります。このインターフェースには、いくつかの簡単なメソッドが含まれています。スタブされたMongoDBの実装は次のようになります。
1<?php 2 3namespace App\Extensions; 4 5class MongoSessionHandler implements \SessionHandlerInterface 6{ 7 public function open($savePath, $sessionName) {} 8 public function close() {} 9 public function read($sessionId) {}10 public function write($sessionId, $data) {}11 public function destroy($sessionId) {}12 public function gc($lifetime) {}13}
Laravelには拡張機能を格納するためのデフォルトのディレクトリが含まれていないため、好きな場所に自由に配置できます。この例では、MongoSessionHandler を格納するために Extensions ディレクトリを作成しました。
これらのメソッドの目的はすぐには理解できないため、各メソッドの目的の概要を以下に示します。
openメソッドは、通常、ファイルベースのセッションストアシステムで使用されます。Laravelにはfileセッションドライバが同梱されているため、このメソッドに何かを入れる必要はほとんどありません。このメソッドは空のままにしておくことができます。closeメソッドは、openメソッドと同様に、通常は無視できます。ほとんどのドライバでは不要です。readメソッドは、指定された$sessionIdに関連付けられたセッションデータの文字列バージョンを返す必要があります。ドライバでセッションデータを取得または保存する際に、シリアル化やその他のエンコーディングを行う必要はありません。Laravelがシリアル化を実行します。writeメソッドは、指定された$data文字列を$sessionIdに関連付けて、MongoDBや選択した他のストレージシステムなどの永続的なストレージシステムに書き込む必要があります。繰り返しになりますが、シリアル化を行うべきではありません - Laravelがすでにそれを処理しています。destroyメソッドは、$sessionIdに関連付けられたデータを永続ストレージから削除する必要があります。gcメソッドは、UNIXタイムスタンプである指定された$lifetimeよりも古いすべてのセッションデータを破棄する必要があります。MemcachedやRedisのような自己期限切れシステムの場合、このメソッドは空のままにしておくことができます。
ドライバの登録
ドライバが実装されたら、Laravelに登録する準備が整いました。Laravelのセッションバックエンドに追加のドライバを追加するには、Session ファサードによって提供される extend メソッドを使用できます。サービスプロバイダの boot メソッドから extend メソッドを呼び出す必要があります。これは既存の App\Providers\AppServiceProvider から行うか、まったく新しいプロバイダを作成することができます。
1<?php 2 3namespace App\Providers; 4 5use App\Extensions\MongoSessionHandler; 6use Illuminate\Contracts\Foundation\Application; 7use Illuminate\Support\Facades\Session; 8use Illuminate\Support\ServiceProvider; 9 10class SessionServiceProvider extends ServiceProvider11{12 /**13 * Register any application services.14 */15 public function register(): void16 {17 // ...18 }19 20 /**21 * Bootstrap any application services.22 */23 public function boot(): void24 {25 Session::extend('mongo', function (Application $app) {26 // Return an implementation of SessionHandlerInterface...27 return new MongoSessionHandler;28 });29 }30}
セッションドライバが登録されたら、SESSION_DRIVER 環境変数を使用するか、アプリケーションの config/session.php 設定ファイル内で、アプリケーションのセッションドライバとして mongo ドライバを指定できます。