[PHP]session_module_name()完全解説|セッションモジュールの取得・変更方法と実践的な使い方

PHP

はじめに

PHPでWebアプリケーションを開発する際、ユーザーのログイン状態やカート情報を保持するためにセッションは欠かせない機能です。セッションのデータをどこにどのように保存するかを決める「セッションモジュール(ハンドラ)」は、アプリケーションのパフォーマンスやスケーラビリティに直結します。

今回解説する session_module_name() は、現在使用中のセッションモジュール名を取得・変更できる関数です。地味に見えて、複数サーバー構成やRedis・Memcachedとの連携時に非常に重要な役割を果たします。


関数の概要

項目内容
関数名session_module_name()
所属PHP セッション関数
導入バージョンPHP 4以降
PHP 8.x対応済み

構文

session_module_name(string $module = ?): string|false

パラメータ

パラメータ説明
$modulestring(省略可)設定したいセッションモジュール名。省略すると現在の値を返すのみ。

戻り値

  • 引数なし(取得):現在のセッションモジュール名(文字列)を返します。
  • 引数あり(変更):変更のモジュール名(文字列)を返します。
  • 失敗時false を返します。

⚠️ 注意session_start() を呼び出したsession_module_name() でモジュールを変更しようとすると、エラーが発生します。必ず session_start()に呼び出してください。


基本的な使い方

モジュール名の取得

<?php
// セッションを開始する前に確認
$moduleName = session_module_name();
echo "現在のセッションモジュール: " . $moduleName;
// 出力例: 現在のセッションモジュール: files

モジュール名の変更

<?php
// session_start() の前に変更する
$oldModule = session_module_name('user'); // カスタムハンドラへ変更
echo "変更前のモジュール: " . $oldModule; // 変更前の名前を返す

session_start();

代表的なモジュール名

モジュール名説明
filesデフォルト。サーバーのファイルシステムに保存
usersession_set_save_handler() で登録したカスタムハンドラを使用
memcacheMemcacheモジュール使用時(pecl/memcache)
memcachedMemcachedモジュール使用時(pecl/memcached)
redisRedisSessionHandler使用時
sqliteSQLiteに保存(古い環境)

実践的なクラスベースの使用例

例1:セッションモジュール診断クラス

<?php
/**
 * セッションモジュール診断クラス
 * 現在の環境でどのモジュールが使用されているかを診断する
 */
class SessionModuleDiagnostics
{
    private array $knownModules = [
        'files'     => 'ファイルシステム(デフォルト)',
        'user'      => 'カスタムユーザーハンドラ',
        'memcache'  => 'Memcache(pecl/memcache)',
        'memcached' => 'Memcached(pecl/memcached)',
        'redis'     => 'Redis',
        'sqlite'    => 'SQLite',
    ];

    public function diagnose(): array
    {
        $current = session_module_name();

        return [
            'module_name'   => $current,
            'description'   => $this->knownModules[$current] ?? '不明なモジュール',
            'save_path'     => session_save_path(),
            'is_file_based' => ($current === 'files'),
            'session_status'=> $this->getStatusLabel(session_status()),
        ];
    }

    private function getStatusLabel(int $status): string
    {
        return match ($status) {
            PHP_SESSION_DISABLED => '無効',
            PHP_SESSION_NONE     => '有効(未開始)',
            PHP_SESSION_ACTIVE   => '有効(開始済み)',
            default              => '不明',
        };
    }

    public function report(): void
    {
        $info = $this->diagnose();
        echo "=== セッションモジュール診断レポート ===\n";
        foreach ($info as $key => $value) {
            printf("%-20s: %s\n", $key, $value);
        }
    }
}

$diag = new SessionModuleDiagnostics();
$diag->report();

/*
出力例:
=== セッションモジュール診断レポート ===
module_name         : files
description         : ファイルシステム(デフォルト)
save_path           : /var/lib/php/sessions
is_file_based       : 1
session_status      : 有効(未開始)
*/

例2:Redis セッションハンドラ切り替えクラス

<?php
/**
 * 環境に応じてセッションストレージを切り替えるクラス
 * 本番環境ではRedis、開発環境ではファイルを使用する例
 */
class SessionStorageConfigurator
{
    private string $environment;
    private array $redisConfig;

    public function __construct(string $environment, array $redisConfig = [])
    {
        $this->environment = $environment;
        $this->redisConfig = array_merge([
            'host' => '127.0.0.1',
            'port' => 6379,
            'auth' => null,
            'ttl'  => 1800,
        ], $redisConfig);
    }

    public function configure(): string
    {
        if (session_status() === PHP_SESSION_ACTIVE) {
            throw new \RuntimeException('セッション開始後は設定を変更できません');
        }

        $previousModule = session_module_name();

        if ($this->environment === 'production' && extension_loaded('redis')) {
            $this->configureRedis();
            $applied = 'redis';
        } else {
            // ファイルシステム(デフォルトのまま)
            $applied = session_module_name(); // 'files'
        }

        echo "環境: {$this->environment}\n";
        echo "変更前モジュール: {$previousModule}\n";
        echo "適用モジュール: {$applied}\n";

        return $applied;
    }

    private function configureRedis(): void
    {
        $dsn = "tcp://{$this->redisConfig['host']}:{$this->redisConfig['port']}";
        if ($this->redisConfig['auth']) {
            $dsn .= "?auth={$this->redisConfig['auth']}";
        }

        ini_set('session.save_handler', 'redis');
        ini_set('session.save_path', $dsn);
        // ※ Redis拡張の設定では session_module_name('redis') ではなく
        //    ini_set で save_handler を設定するケースもある
    }
}

$configurator = new SessionStorageConfigurator('development');
$configurator->configure();

例3:カスタムDBセッションハンドラクラス

<?php
/**
 * MySQLにセッションデータを保存するカスタムハンドラ
 * session_module_name('user') と組み合わせて使用する
 */
class DatabaseSessionHandler implements \SessionHandlerInterface
{
    private \PDO $pdo;

    public function __construct(\PDO $pdo)
    {
        $this->pdo = $pdo;
    }

    public function open(string $path, string $name): bool
    {
        return true;
    }

    public function close(): bool
    {
        return true;
    }

    public function read(string $id): string|false
    {
        $stmt = $this->pdo->prepare(
            "SELECT data FROM sessions WHERE id = :id AND expires_at > NOW()"
        );
        $stmt->execute([':id' => $id]);
        $row = $stmt->fetch(\PDO::FETCH_ASSOC);
        return $row ? $row['data'] : '';
    }

    public function write(string $id, string $data): bool
    {
        $stmt = $this->pdo->prepare(
            "INSERT INTO sessions (id, data, expires_at)
             VALUES (:id, :data, DATE_ADD(NOW(), INTERVAL :ttl SECOND))
             ON DUPLICATE KEY UPDATE data = :data, expires_at = DATE_ADD(NOW(), INTERVAL :ttl SECOND)"
        );
        return $stmt->execute([
            ':id'   => $id,
            ':data' => $data,
            ':ttl'  => (int) ini_get('session.gc_maxlifetime'),
        ]);
    }

    public function destroy(string $id): bool
    {
        $stmt = $this->pdo->prepare("DELETE FROM sessions WHERE id = :id");
        return $stmt->execute([':id' => $id]);
    }

    public function gc(int $max_lifetime): int|false
    {
        $stmt = $this->pdo->prepare("DELETE FROM sessions WHERE expires_at < NOW()");
        $stmt->execute();
        return $stmt->rowCount();
    }
}

// --- 登録と利用 ---
/*
$pdo     = new PDO('mysql:host=localhost;dbname=myapp', 'user', 'pass');
$handler = new DatabaseSessionHandler($pdo);
session_set_save_handler($handler, true);

// 'user' モジュールを明示的に指定
$prev = session_module_name('user');
echo "変更前: {$prev}\n"; // files

session_start();
$_SESSION['user_id'] = 42;
*/

例4:セッションモジュール切り替えファクトリ

<?php
/**
 * 利用可能な拡張に応じて最適なセッションモジュールを自動選択するクラス
 */
class SessionModuleFactory
{
    /** 優先順位順のモジュール候補 */
    private array $preferenceList;

    public function __construct(array $preferenceList = [])
    {
        $this->preferenceList = $preferenceList ?: [
            ['module' => 'redis',     'extension' => 'redis'],
            ['module' => 'memcached', 'extension' => 'memcached'],
            ['module' => 'memcache',  'extension' => 'memcache'],
            ['module' => 'files',     'extension' => null],  // フォールバック
        ];
    }

    public function selectBest(): string
    {
        foreach ($this->preferenceList as $candidate) {
            $ext = $candidate['extension'];
            if ($ext === null || extension_loaded($ext)) {
                echo "選択されたモジュール: {$candidate['module']}\n";
                return $candidate['module'];
            }
            echo "スキップ(拡張なし): {$candidate['module']}\n";
        }
        return 'files'; // 最終フォールバック
    }

    public function applyBest(): string
    {
        if (session_status() === PHP_SESSION_ACTIVE) {
            throw new \RuntimeException('セッション開始後は変更不可');
        }

        $best = $this->selectBest();
        $prev = session_module_name(); // 現在値を取得

        if ($best !== 'files' && $best !== 'user') {
            // Redisなどは ini_set で設定するため、ここではログのみ
            echo "モジュール '{$best}' の適用には別途 ini_set が必要な場合があります\n";
        }

        echo "適用前モジュール: {$prev}\n";
        return $best;
    }
}

$factory = new SessionModuleFactory();
$factory->applyBest();

/*
出力例(Redis拡張なし、Memcachedあり):
スキップ(拡張なし): redis
選択されたモジュール: memcached
適用前モジュール: files
モジュール 'memcached' の適用には別途 ini_set が必要な場合があります
*/

例5:セッションモジュールのユニットテスト補助クラス

<?php
/**
 * テスト時にセッションモジュールを安全に切り替え・復元するクラス
 * PHPUnit などのテスト環境での利用を想定
 */
class SessionModuleSwitcher
{
    private ?string $originalModule = null;
    private bool $wasActive = false;

    public function setUp(string $targetModule = 'user'): void
    {
        // セッションが動いていれば一旦閉じる
        if (session_status() === PHP_SESSION_ACTIVE) {
            $this->wasActive = true;
            session_write_close();
        }

        $this->originalModule = session_module_name();
        echo "テスト用モジュール設定: {$this->originalModule} → {$targetModule}\n";
        session_module_name($targetModule);
    }

    public function tearDown(): void
    {
        if ($this->originalModule !== null) {
            if (session_status() === PHP_SESSION_ACTIVE) {
                session_write_close();
            }
            session_module_name($this->originalModule);
            echo "モジュール復元: {$this->originalModule}\n";
            $this->originalModule = null;
        }
    }

    public function getCurrentModule(): string
    {
        return session_module_name();
    }
}

// テスト利用イメージ
$switcher = new SessionModuleSwitcher();
$switcher->setUp('user');
echo "現在のモジュール: " . $switcher->getCurrentModule() . "\n";
// ... テスト処理 ...
$switcher->tearDown();

例6:セッションモジュール情報をJSON出力する監視クラス

<?php
/**
 * セッションモジュールの状態を監視・JSON出力するクラス
 * ヘルスチェックエンドポイントや管理画面での利用を想定
 */
class SessionModuleMonitor
{
    public function getStatus(): array
    {
        $module = session_module_name();
        $savePath = session_save_path();

        $status = [
            'module'        => $module,
            'save_path'     => $savePath ?: '(php.ini のデフォルト)',
            'session_id'    => session_id() ?: null,
            'gc_maxlifetime'=> (int) ini_get('session.gc_maxlifetime'),
            'cookie_secure' => (bool) ini_get('session.cookie_secure'),
            'cookie_httponly'=> (bool) ini_get('session.cookie_httponly'),
            'use_strict_mode'=> (bool) ini_get('session.use_strict_mode'),
            'checked_at'    => date('Y-m-d H:i:s'),
        ];

        // モジュール固有の警告
        $status['warnings'] = $this->getWarnings($module, $status);

        return $status;
    }

    private function getWarnings(string $module, array $status): array
    {
        $warnings = [];

        if ($module === 'files' && php_uname('s') === 'Linux') {
            $warnings[] = '複数サーバー構成ではセッション共有ができません。Redisなどを検討してください。';
        }

        if (!$status['cookie_secure']) {
            $warnings[] = 'session.cookie_secure が無効です。HTTPS環境では有効化を推奨します。';
        }

        if (!$status['cookie_httponly']) {
            $warnings[] = 'session.cookie_httponly が無効です。XSS対策のため有効化を推奨します。';
        }

        if (!$status['use_strict_mode']) {
            $warnings[] = 'session.use_strict_mode が無効です。セッション固定攻撃への耐性を高めるため有効化を推奨します。';
        }

        return $warnings;
    }

    public function toJson(): string
    {
        return json_encode($this->getStatus(), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
    }
}

$monitor = new SessionModuleMonitor();
echo $monitor->toJson();

/*
出力例:
{
    "module": "files",
    "save_path": "(php.ini のデフォルト)",
    "session_id": null,
    "gc_maxlifetime": 1440,
    "cookie_secure": false,
    "cookie_httponly": false,
    "use_strict_mode": false,
    "checked_at": "2025-09-01 12:00:00",
    "warnings": [
        "複数サーバー構成ではセッション共有ができません。Redisなどを検討してください。",
        "session.cookie_secure が無効です。HTTPS環境では有効化を推奨します。",
        ...
    ]
}
*/

関連関数との比較

関数役割
session_module_name()セッションモジュール(ハンドラ種別)の取得・変更
session_set_save_handler()user モジュール用のカスタム保存ハンドラを登録
session_save_path()セッションデータの保存先パス(URL)を取得・変更
session_start()セッションを開始(これより前に設定が必要)
ini_set('session.save_handler', ...)php.ini レベルでハンドラを変更(Redis等で使用)

session_module_name() と ini_set() の使い分け

  • filesuser の切り替えsession_module_name() が直接使える
  • Redis・Memcached の設定:多くの場合 ini_set('session.save_handler', 'redis')ini_set('session.save_path', ...) の組み合わせを使う
  • どちらも session_start() より前に呼ぶ必要がある点は共通

よくある使用シーンまとめ

  1. 現在のモジュール確認:デバッグや管理画面でのヘルスチェック
  2. テスト環境でのモジュール切り替え:ファイルベースとカスタムハンドラを切り替えてテスト
  3. カスタムDBハンドラ登録時session_set_save_handler() と組み合わせて 'user' を指定
  4. 環境別設定:開発では files、本番では Redis という設定の自動切り替え
  5. セキュリティ監査:セッション設定の一括チェックと警告出力

注意点・落とし穴

<?php
// ❌ NG:session_start() の後で変更しようとするとエラー
session_start();
session_module_name('user'); // Warning: Cannot change save handler when session is active

// ✅ OK:session_start() の前に変更する
session_module_name('user');
session_start();
<?php
// ❌ NG:'redis' を直接指定してもRedis拡張が入っていなければ機能しない
session_module_name('redis'); // 拡張がなければセッション開始時にエラー

// ✅ OK:拡張の存在を確認してから設定する
if (extension_loaded('redis')) {
    ini_set('session.save_handler', 'redis');
    ini_set('session.save_path', 'tcp://127.0.0.1:6379');
}
session_start();

まとめ

項目内容
関数名session_module_name(?string $module): string|false
主な用途セッション保存モジュールの取得・変更
呼び出しタイミング必ず session_start() より前
戻り値現在(または変更前)のモジュール名
デフォルト値files
カスタムハンドラ使用時'user' を指定し session_set_save_handler() と併用

session_module_name() は一見地味な関数ですが、スケールアウトが必要な本番環境や、テストコードの品質向上において非常に重要な役割を担います。デフォルトの files モジュールで問題ない小規模アプリでも、将来の拡張に備えてこの関数の存在を覚えておきましょう。


参考リンク

タイトルとURLをコピーしました