コンテンツへスキップ

HTTPセッション

はじめに

HTTP駆動アプリケーションはステートレスであるため、セッションは複数のリクエストにわたってユーザーに関する情報を保存する方法を提供します。そのユーザー情報は通常、後続のリクエストからアクセスできる永続的なストア/バックエンドに配置されます。

Laravelには、表現力豊かで統一されたAPIを通じてアクセスできるさまざまなセッションバックエンドが付属しています。 MemcachedRedis、データベースなどの一般的なバックエンドのサポートが含まれています。

設定

アプリケーションのセッション構成ファイルはconfig/session.phpに保存されています。このファイルで利用可能なオプションを必ず確認してください。デフォルトでは、Laravelはdatabaseセッションドライバを使用するように構成されています。

セッションのdriver構成オプションは、各リクエストのセッションデータが保存される場所を定義します。Laravelにはさまざまなドライバが含まれています

  • file - セッションはstorage/framework/sessionsに保存されます。
  • cookie - セッションは、安全な暗号化されたCookieに保存されます。
  • database - セッションは、リレーショナルデータベースに保存されます。
  • memcached / redis - セッションは、これらの高速なキャッシュベースのストアのいずれかに保存されます。
  • dynamodb - セッションは、AWS DynamoDBに保存されます。
  • array - セッションはPHP配列に保存され、永続化されません。
lightbulb

配列ドライバは、主にテスト中に使用され、セッションに保存されたデータが永続化されるのを防ぎます。

ドライバの前提条件

データベース

databaseセッションドライバを使用する場合は、セッションデータを格納するデータベーステーブルがあることを確認する必要があります。通常、これはLaravelのデフォルトの0001_01_01_000000_create_users_table.php データベースマイグレーションに含まれています。ただし、何らかの理由でsessionsテーブルがない場合は、make:session-table Artisanコマンドを使用してこのマイグレーションを生成できます。

php artisan make:session-table
 
php artisan migrate

Redis

LaravelでRedisセッションを使用する前に、PECL経由でPhpRedis PHP拡張機能をインストールするか、Composer経由でpredis/predisパッケージ(〜1.0)をインストールする必要があります。Redisの構成の詳細については、LaravelのRedisドキュメントを参照してください。

lightbulb

SESSION_CONNECTION環境変数、またはsession.php構成ファイルのconnectionオプションを使用して、セッションストレージに使用するRedis接続を指定できます。

セッションの操作

データの取得

Laravelでセッションデータを操作するには、グローバルsessionヘルパーとRequestインスタンスを介した2つの主要な方法があります。最初に、ルートクロージャまたはコントローラメソッドでタイプヒントできるRequestインスタンスを介してセッションにアクセスする方法を見てみましょう。コントローラメソッドの依存関係は、Laravelのサービスコンテナを介して自動的に注入されることを覚えておいてください。

<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use Illuminate\View\View;
 
class UserController extends Controller
{
/**
* Show the profile for the given user.
*/
public function show(Request $request, string $id): View
{
$value = $request->session()->get('key');
 
// ...
 
$user = $this->users->find($id);
 
return view('user.profile', ['user' => $user]);
}
}

セッションからアイテムを取得するときは、getメソッドへの2番目の引数としてデフォルト値を渡すこともできます。指定されたキーがセッションに存在しない場合、このデフォルト値が返されます。getメソッドのデフォルト値としてクロージャを渡し、要求されたキーが存在しない場合、クロージャが実行され、その結果が返されます

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

グローバルセッションヘルパー

グローバルsessionPHP関数を使用して、セッションにデータを取得および保存することもできます。sessionヘルパーが単一の文字列引数で呼び出されると、そのセッションキーの値が返されます。ヘルパーがキー/値ペアの配列で呼び出されると、それらの値がセッションに保存されます

Route::get('/home', function () {
// Retrieve a piece of data from the session...
$value = session('key');
 
// Specifying a default value...
$value = session('key', 'default');
 
// Store a piece of data in the session...
session(['key' => 'value']);
});
lightbulb

HTTPリクエストインスタンスを介してセッションを使用する場合と、グローバルsessionヘルパーを使用する場合との間に、実質的な違いはほとんどありません。両方のメソッドは、すべてのテストケースで利用可能なassertSessionHasメソッドを介してテスト可能です。

すべてのセッションデータの取得

セッション内のすべてのデータを取得する場合は、allメソッドを使用できます

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

セッションデータの一部を取得

onlyおよびexceptメソッドを使用して、セッションデータのサブセットを取得できます

$data = $request->session()->only(['username', 'email']);
 
$data = $request->session()->except(['username', 'email']);

セッションにアイテムが存在するかどうかの確認

セッションにアイテムが存在するかどうかを確認するには、hasメソッドを使用できます。hasメソッドは、アイテムが存在し、nullではない場合にtrueを返します

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

値がnullであっても、セッションにアイテムが存在するかどうかを確認するには、existsメソッドを使用できます

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

セッションにアイテムが存在しないかどうかを確認するには、missingメソッドを使用できます。missingメソッドは、アイテムが存在しない場合にtrueを返します

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

データの保存

セッションにデータを保存するには、通常、リクエストインスタンスのputメソッドまたはグローバルsessionヘルパーを使用します

// Via a request instance...
$request->session()->put('key', 'value');
 
// Via the global "session" helper...
session(['key' => 'value']);

配列セッション値へのプッシュ

pushメソッドは、配列であるセッション値に新しい値をプッシュするために使用できます。たとえば、user.teamsキーにチーム名の配列が含まれている場合、次のように配列に新しい値をプッシュできます

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

アイテムの取得と削除

pullメソッドは、単一のステートメントでセッションからアイテムを取得して削除します

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

セッション値の増減

セッションデータに増減したい整数が含まれている場合は、incrementおよびdecrementメソッドを使用できます

$request->session()->increment('count');
 
$request->session()->increment('count', $incrementBy = 2);
 
$request->session()->decrement('count');
 
$request->session()->decrement('count', $decrementBy = 2);

フラッシュデータ

次のリクエストのためにアイテムをセッションに保存したい場合があります。flashメソッドを使用してそれを行うことができます。このメソッドを使用してセッションに保存されたデータは、すぐ、そして後続のHTTPリクエスト中に利用可能になります。後続のHTTPリクエストの後、フラッシュデータは削除されます。フラッシュデータは、主に短命のステータスメッセージに役立ちます

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

フラッシュデータを複数のリクエストにわたって永続化する必要がある場合は、reflashメソッドを使用できます。これにより、すべてのフラッシュデータが追加のリクエストのために保持されます。特定のフラッシュデータのみを保持する必要がある場合は、keepメソッドを使用できます

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

フラッシュデータを現在のリクエストでのみ永続化するには、nowメソッドを使用できます

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

データの削除

forgetメソッドは、セッションからデータの一部を削除します。セッションからすべてのデータを削除したい場合は、flushメソッドを使用できます。

// Forget a single key...
$request->session()->forget('name');
 
// Forget multiple keys...
$request->session()->forget(['name', 'status']);
 
$request->session()->flush();

セッションIDの再生成

セッションIDの再生成は、悪意のあるユーザーがアプリケーションに対するセッション固定攻撃を悪用するのを防ぐためによく行われます。

Laravelでは、LaravelのアプリケーションスターターキットまたはLaravel Fortifyのいずれかを使用している場合、認証中にセッションIDが自動的に再生成されます。ただし、セッションIDを手動で再生成する必要がある場合は、regenerateメソッドを使用できます。

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

セッションIDを再生成し、セッションからすべてのデータを1つのステートメントで削除する必要がある場合は、invalidateメソッドを使用できます。

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

セッションブロッキング

exclamation

セッションブロッキングを利用するには、アプリケーションがアトミックロックをサポートするキャッシュドライバーを使用している必要があります。現在、これらのキャッシュドライバーには、memcacheddynamodbredismongodb(公式のmongodb/laravel-mongodbパッケージに含まれています)、databasefile、およびarrayドライバーが含まれています。さらに、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\LockTimeoutExceptionがスローされます。

これらの引数のいずれも渡されない場合、ロックは最大10秒間取得され、リクエストはロックの取得を試行中に最大10秒間待機します。

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

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

ドライバの実装

既存のセッションドライバーがアプリケーションのニーズに合わない場合、Laravelでは独自のセッションハンドラーを作成できます。カスタムセッションドライバーは、PHPの組み込み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) {}
}

Laravelには拡張機能を格納するためのデフォルトのディレクトリが含まれていないため、どこにでも自由に配置できます。この例では、MongoSessionHandlerを格納するためにExtensionsディレクトリを作成しました。

これらのメソッドの目的は容易に理解できないため、各メソッドの目的の概要を次に示します。

  • openメソッドは、通常、ファイルベースのセッションストアシステムで使用されます。Laravelにはfileセッションドライバーが付属しているため、このメソッドに何かを入れる必要はほとんどありません。このメソッドは空のままにしておくことができます。
  • closeメソッドは、openメソッドと同様に、通常は無視できます。ほとんどのドライバーでは必要ありません。
  • readメソッドは、指定された$sessionIdに関連付けられたセッションデータの文字列バージョンを返す必要があります。Laravelがシリアル化を実行するため、ドライバーでセッションデータを取得または保存するときに、シリアル化やその他のエンコードを行う必要はありません。
  • writeメソッドは、指定された$data文字列を$sessionIdに関連付けて、MongoDBなどの永続ストレージシステムまたは選択した別のストレージシステムに書き込む必要があります。繰り返しますが、シリアル化を実行しないでください。Laravelが既に処理しています。
  • destroyメソッドは、$sessionIdに関連付けられたデータを永続ストレージから削除する必要があります。
  • gcメソッドは、指定された$lifetime(UNIXタイムスタンプ)よりも古いすべてのセッションデータを破棄する必要があります。MemcachedやRedisのような自己期限切れシステムの場合、このメソッドは空のままにできます。

ドライバの登録

ドライバーが実装されたら、Laravelに登録する準備ができました。Laravelのセッションバックエンドにドライバーを追加するには、Sessionファサードによって提供されるextendメソッドを使用できます。extendメソッドは、サービスプロバイダーbootメソッドから呼び出す必要があります。既存のApp\Providers\AppServiceProviderからこれを行うか、まったく新しいプロバイダーを作成できます。

<?php
 
namespace App\Providers;
 
use App\Extensions\MongoSessionHandler;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\ServiceProvider;
 
class SessionServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
// ...
}
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Session::extend('mongo', function (Application $app) {
// Return an implementation of SessionHandlerInterface...
return new MongoSessionHandler;
});
}
}

セッションドライバーが登録されたら、SESSION_DRIVER環境変数を使用するか、アプリケーションのconfig/session.php構成ファイル内で、アプリケーションのセッションドライバーとしてmongoドライバーを指定できます。