[PHP]session_id関数を完全解説!セッションIDを取得・設定する方法

PHP

こんにちは!今回は、PHPの標準関数であるsession_id()について詳しく解説していきます。セッションIDの取得と設定ができる、セッション管理の基本となる重要な関数です!

session_id関数とは?

session_id()関数は、現在のセッションIDを取得、または新しいセッションIDを設定する関数です。

セッション開始前にIDを設定したり、開始後に現在のIDを確認したりできます。セッション固定攻撃対策やマルチデバイス管理などに使用されます!

基本的な構文

session_id(?string $id = null): string|false
  • $id: 設定する新しいセッションID(省略時は取得のみ)
  • 戻り値: 現在のセッションID、失敗時はfalse

重要な注意点

// セッション開始前に設定、開始後に取得

// 取得(セッション開始後)
session_start();
$sessionId = session_id();
echo "現在のセッションID: {$sessionId}\n";

// 設定(セッション開始前)
$newId = 'custom_session_id_12345';
session_id($newId);
session_start();
echo "設定したID: " . session_id() . "\n";

// セッション開始後の設定は無効
session_start();
session_id('new_id');  // 効果なし
echo session_id();     // 元のIDのまま

// セッションIDの形式制限
// 英数字とカンマ(,)、ハイフン(-)のみ使用可能
// 無効な文字を含むとエラー

// 空文字列でセッションIDをクリア(開始前のみ)
session_id('');
session_start();  // 新しいIDが自動生成される

// session_regenerate_id()との違い
session_start();
session_regenerate_id();  // セッション継続中にIDを再生成(推奨)
// vs
session_destroy();
session_id('new_id');
session_start();  // 完全に新しいセッション

セッションIDの特性

// セッションIDの長さと形式

session_start();
$id = session_id();

echo "セッションID: {$id}\n";
echo "長さ: " . strlen($id) . " 文字\n";

// デフォルトの長さは設定により異なる
// session.sid_length (PHP 7.1.0以降、デフォルト32)
// session.hash_bits_per_character (デフォルト4)

echo "sid_length: " . ini_get('session.sid_length') . "\n";
echo "hash_bits_per_character: " . ini_get('session.hash_bits_per_character') . "\n";

// セッションIDの一意性
// PHPが自動生成するIDは暗号的に安全な乱数
// 重複の可能性は極めて低い

// セッションIDとファイル名の関係
$sessionPath = session_save_path();
if (empty($sessionPath)) {
    $sessionPath = sys_get_temp_dir();
}
$filename = $sessionPath . '/sess_' . session_id();
echo "セッションファイル: {$filename}\n";

基本的な使用例

セッションIDの取得

// セッション開始
session_start();

// 現在のセッションIDを取得
$sessionId = session_id();

echo "セッションID: {$sessionId}\n";
echo "ID長: " . strlen($sessionId) . " 文字\n";

// セッション名も確認
$sessionName = session_name();
echo "セッション名: {$sessionName}\n";

// クッキーに保存されているか確認
if (isset($_COOKIE[$sessionName])) {
    echo "クッキーのID: {$_COOKIE[$sessionName]}\n";
    echo "一致: " . ($sessionId === $_COOKIE[$sessionName] ? 'Yes' : 'No') . "\n";
}

カスタムセッションIDの設定

// カスタムIDを設定(セッション開始前)
$customId = bin2hex(random_bytes(16));  // 32文字の16進数
echo "カスタムID: {$customId}\n";

session_id($customId);
session_start();

// 設定されたか確認
echo "現在のID: " . session_id() . "\n";
echo "一致: " . ($customId === session_id() ? 'Yes' : 'No') . "\n";

セッションIDの検証

// セッションIDの形式を検証
function validateSessionId($id) {
    // 長さチェック(通常22-128文字)
    if (strlen($id) < 22 || strlen($id) > 128) {
        return false;
    }
    
    // 文字チェック(英数字、カンマ、ハイフン)
    if (!preg_match('/^[a-zA-Z0-9,-]+$/', $id)) {
        return false;
    }
    
    return true;
}

// テスト
$validId = session_id();
$invalidId = 'invalid@id#123';

echo "Valid ID: " . ($validId ? 'Yes' : 'No') . " - " . 
     (validateSessionId($validId) ? 'OK' : 'NG') . "\n";
echo "Invalid ID: " . (validateSessionId($invalidId) ? 'OK' : 'NG') . "\n";

セッションIDの比較

session_start();
$id1 = session_id();

// セッションを終了
session_write_close();

// 新しいセッションを開始
session_start();
$id2 = session_id();

echo "1回目のID: {$id1}\n";
echo "2回目のID: {$id2}\n";
echo "同じID: " . ($id1 === $id2 ? 'Yes' : 'No') . "\n";
// 通常は同じID(クッキーが保持されている場合)

実践的な使用例

例1: セッションID管理システム

class SessionIdManager {
    private $idHistory = [];
    
    /**
     * 現在のセッションIDを取得
     */
    public function getCurrentId() {
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
        
        $id = session_id();
        
        return [
            'session_id' => $id,
            'length' => strlen($id),
            'session_name' => session_name(),
            'session_status' => $this->getStatusName(session_status()),
            'created_at' => $_SESSION['created_at'] ?? null
        ];
    }
    
    /**
     * セッションステータス名を取得
     */
    private function getStatusName($status) {
        switch ($status) {
            case PHP_SESSION_DISABLED:
                return 'disabled';
            case PHP_SESSION_NONE:
                return 'none';
            case PHP_SESSION_ACTIVE:
                return 'active';
            default:
                return 'unknown';
        }
    }
    
    /**
     * セッションIDを再生成
     */
    public function regenerateId($deleteOldSession = true) {
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
        
        $oldId = session_id();
        
        // 再生成
        session_regenerate_id($deleteOldSession);
        
        $newId = session_id();
        
        // 履歴に記録
        $this->idHistory[] = [
            'old_id' => $oldId,
            'new_id' => $newId,
            'timestamp' => time(),
            'delete_old' => $deleteOldSession
        ];
        
        // 再生成時刻を記録
        $_SESSION['last_regenerated'] = time();
        
        return [
            'old_id' => $oldId,
            'new_id' => $newId,
            'regenerated_at' => time()
        ];
    }
    
    /**
     * カスタムセッションIDで開始
     */
    public function startWithCustomId($customId = null) {
        if (session_status() === PHP_SESSION_ACTIVE) {
            throw new Exception("Session already started");
        }
        
        // カスタムIDが指定されていない場合は生成
        if ($customId === null) {
            $customId = $this->generateSecureId();
        }
        
        // バリデーション
        if (!$this->validateId($customId)) {
            throw new Exception("Invalid session ID format");
        }
        
        session_id($customId);
        session_start();
        
        $_SESSION['created_at'] = time();
        $_SESSION['custom_id'] = true;
        
        return [
            'session_id' => session_id(),
            'custom' => true,
            'started_at' => time()
        ];
    }
    
    /**
     * セキュアなセッションIDを生成
     */
    public function generateSecureId($length = 32) {
        // 暗号的に安全な乱数を生成
        $bytes = random_bytes($length);
        return bin2hex($bytes);
    }
    
    /**
     * セッションIDを検証
     */
    public function validateId($id) {
        // 長さチェック
        $minLength = 22;
        $maxLength = 128;
        
        if (strlen($id) < $minLength || strlen($id) > $maxLength) {
            return false;
        }
        
        // 文字チェック(英数字、カンマ、ハイフン)
        if (!preg_match('/^[a-zA-Z0-9,-]+$/', $id)) {
            return false;
        }
        
        return true;
    }
    
    /**
     * セッションID情報を取得
     */
    public function getIdInfo() {
        if (session_status() !== PHP_SESSION_ACTIVE) {
            return [
                'error' => 'Session not active',
                'status' => 'inactive'
            ];
        }
        
        $id = session_id();
        
        return [
            'id' => $id,
            'length' => strlen($id),
            'valid' => $this->validateId($id),
            'entropy' => $this->calculateEntropy($id),
            'age' => isset($_SESSION['created_at']) ? time() - $_SESSION['created_at'] : null,
            'regeneration_count' => count($this->idHistory),
            'last_regenerated' => $_SESSION['last_regenerated'] ?? null
        ];
    }
    
    /**
     * エントロピーを計算(簡易版)
     */
    private function calculateEntropy($string) {
        $chars = str_split($string);
        $frequency = array_count_values($chars);
        $length = strlen($string);
        
        $entropy = 0;
        foreach ($frequency as $count) {
            $p = $count / $length;
            $entropy -= $p * log($p, 2);
        }
        
        return round($entropy, 2);
    }
    
    /**
     * セッションファイルのパスを取得
     */
    public function getSessionFilePath() {
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
        
        $sessionPath = session_save_path();
        if (empty($sessionPath)) {
            $sessionPath = sys_get_temp_dir();
        }
        
        $id = session_id();
        $filePath = $sessionPath . '/sess_' . $id;
        
        return [
            'directory' => $sessionPath,
            'filename' => 'sess_' . $id,
            'full_path' => $filePath,
            'exists' => file_exists($filePath),
            'size' => file_exists($filePath) ? filesize($filePath) : null,
            'modified' => file_exists($filePath) ? filemtime($filePath) : null
        ];
    }
    
    /**
     * IDの履歴を取得
     */
    public function getHistory() {
        return $this->idHistory;
    }
    
    /**
     * セッションIDを比較
     */
    public function compareIds($id1, $id2) {
        return [
            'id1' => $id1,
            'id2' => $id2,
            'identical' => $id1 === $id2,
            'length_match' => strlen($id1) === strlen($id2),
            'similarity' => similar_text($id1, $id2, $percent),
            'similarity_percent' => round($percent, 2)
        ];
    }
    
    /**
     * レポートを生成
     */
    public function generateReport() {
        $current = $this->getCurrentId();
        $info = $this->getIdInfo();
        $file = $this->getSessionFilePath();
        
        $report = "=== Session ID Report ===\n\n";
        $report .= "Current ID: {$current['session_id']}\n";
        $report .= "Length: {$current['length']} characters\n";
        $report .= "Status: {$current['session_status']}\n";
        $report .= "Valid: " . ($info['valid'] ? 'Yes' : 'No') . "\n";
        $report .= "Entropy: {$info['entropy']}\n";
        
        if ($info['age'] !== null) {
            $report .= "Age: {$info['age']} seconds\n";
        }
        
        $report .= "\nSession File:\n";
        $report .= "Path: {$file['full_path']}\n";
        $report .= "Exists: " . ($file['exists'] ? 'Yes' : 'No') . "\n";
        
        if ($file['exists']) {
            $report .= "Size: {$file['size']} bytes\n";
            $report .= "Modified: " . date('Y-m-d H:i:s', $file['modified']) . "\n";
        }
        
        if (!empty($this->idHistory)) {
            $report .= "\nRegeneration History:\n";
            foreach ($this->idHistory as $entry) {
                $report .= "  " . date('Y-m-d H:i:s', $entry['timestamp']) . 
                          " - {$entry['old_id']} → {$entry['new_id']}\n";
            }
        }
        
        return $report;
    }
}

// 使用例
echo "=== セッションID管理システム ===\n";

$manager = new SessionIdManager();

// セッション開始
session_start();
echo "\n現在のセッション情報:\n";
$current = $manager->getCurrentId();
echo "ID: {$current['session_id']}\n";
echo "長さ: {$current['length']}\n";
echo "ステータス: {$current['session_status']}\n";

// セッションID情報
echo "\n詳細情報:\n";
$info = $manager->getIdInfo();
echo "エントロピー: {$info['entropy']}\n";
echo "有効: " . ($info['valid'] ? 'Yes' : 'No') . "\n";

// セッションファイル情報
echo "\nファイル情報:\n";
$file = $manager->getSessionFilePath();
echo "パス: {$file['full_path']}\n";
echo "存在: " . ($file['exists'] ? 'Yes' : 'No') . "\n";

// ID再生成
echo "\nID再生成:\n";
$regen = $manager->regenerateId(true);
echo "旧ID: {$regen['old_id']}\n";
echo "新ID: {$regen['new_id']}\n";

// もう一度再生成
$regen2 = $manager->regenerateId(true);
echo "2回目 - 新ID: {$regen2['new_id']}\n";

// 履歴確認
echo "\n再生成履歴:\n";
foreach ($manager->getHistory() as $entry) {
    echo date('H:i:s', $entry['timestamp']) . " - ID変更\n";
}

// レポート生成
echo "\n";
echo $manager->generateReport();

// カスタムIDでの開始(新しいセッション用)
session_write_close();
echo "\n=== カスタムIDでの開始 ===\n";
$customStart = $manager->startWithCustomId();
echo "カスタムID: {$customStart['session_id']}\n";

例2: マルチデバイスセッション管理

class MultiDeviceSessionManager {
    private $storage = [];  // 実際の実装ではデータベースを使用
    
    /**
     * デバイスセッションを作成
     */
    public function createDeviceSession($userId, $deviceInfo) {
        // 新しいセッションIDを生成
        $sessionId = bin2hex(random_bytes(32));
        
        // デバイス情報を記録
        $this->storage[$sessionId] = [
            'user_id' => $userId,
            'session_id' => $sessionId,
            'device_info' => $deviceInfo,
            'created_at' => time(),
            'last_activity' => time(),
            'ip_address' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown'
        ];
        
        // セッションIDを設定して開始
        session_id($sessionId);
        session_start();
        
        $_SESSION['user_id'] = $userId;
        $_SESSION['device_info'] = $deviceInfo;
        $_SESSION['created_at'] = time();
        
        return [
            'session_id' => $sessionId,
            'device' => $deviceInfo['type'],
            'created' => true
        ];
    }
    
    /**
     * ユーザーのすべてのセッションを取得
     */
    public function getUserSessions($userId) {
        $sessions = [];
        
        foreach ($this->storage as $sessionId => $data) {
            if ($data['user_id'] === $userId) {
                $sessions[] = [
                    'session_id' => $sessionId,
                    'device_type' => $data['device_info']['type'] ?? 'unknown',
                    'device_name' => $data['device_info']['name'] ?? 'unknown',
                    'created_at' => $data['created_at'],
                    'last_activity' => $data['last_activity'],
                    'age' => time() - $data['created_at'],
                    'is_current' => $sessionId === session_id()
                ];
            }
        }
        
        // 最新順にソート
        usort($sessions, function($a, $b) {
            return $b['last_activity'] - $a['last_activity'];
        });
        
        return $sessions;
    }
    
    /**
     * 特定のセッションを終了
     */
    public function terminateSession($sessionId) {
        if (!isset($this->storage[$sessionId])) {
            return [
                'success' => false,
                'error' => 'Session not found'
            ];
        }
        
        $sessionData = $this->storage[$sessionId];
        
        // セッションファイルを削除
        $sessionPath = session_save_path();
        if (empty($sessionPath)) {
            $sessionPath = sys_get_temp_dir();
        }
        
        $sessionFile = $sessionPath . '/sess_' . $sessionId;
        if (file_exists($sessionFile)) {
            unlink($sessionFile);
        }
        
        // ストレージから削除
        unset($this->storage[$sessionId]);
        
        return [
            'success' => true,
            'session_id' => $sessionId,
            'device' => $sessionData['device_info']['type'] ?? 'unknown'
        ];
    }
    
    /**
     * 他のすべてのセッションを終了
     */
    public function terminateOtherSessions($userId, $currentSessionId = null) {
        if ($currentSessionId === null) {
            $currentSessionId = session_id();
        }
        
        $terminated = 0;
        $sessions = $this->getUserSessions($userId);
        
        foreach ($sessions as $session) {
            if ($session['session_id'] !== $currentSessionId) {
                $result = $this->terminateSession($session['session_id']);
                if ($result['success']) {
                    $terminated++;
                }
            }
        }
        
        return [
            'terminated' => $terminated,
            'current_session' => $currentSessionId
        ];
    }
    
    /**
     * セッションをスイッチ
     */
    public function switchSession($newSessionId) {
        if (!isset($this->storage[$newSessionId])) {
            throw new Exception("Session not found: {$newSessionId}");
        }
        
        $oldSessionId = session_id();
        
        // 現在のセッションを終了
        session_write_close();
        
        // 新しいセッションに切り替え
        session_id($newSessionId);
        session_start();
        
        // 最終アクティビティを更新
        $this->storage[$newSessionId]['last_activity'] = time();
        
        return [
            'old_session' => $oldSessionId,
            'new_session' => $newSessionId,
            'switched' => true
        ];
    }
    
    /**
     * セッション情報を更新
     */
    public function updateActivity($sessionId = null) {
        if ($sessionId === null) {
            $sessionId = session_id();
        }
        
        if (isset($this->storage[$sessionId])) {
            $this->storage[$sessionId]['last_activity'] = time();
            return true;
        }
        
        return false;
    }
    
    /**
     * 非アクティブなセッションをクリーンアップ
     */
    public function cleanupInactiveSessions($timeout = 3600) {
        $cleaned = 0;
        $cutoff = time() - $timeout;
        
        foreach ($this->storage as $sessionId => $data) {
            if ($data['last_activity'] < $cutoff) {
                $this->terminateSession($sessionId);
                $cleaned++;
            }
        }
        
        return [
            'cleaned' => $cleaned,
            'timeout' => $timeout
        ];
    }
    
    /**
     * デバイス別セッション統計
     */
    public function getSessionStatistics() {
        $stats = [
            'total_sessions' => count($this->storage),
            'by_device' => [],
            'by_user' => [],
            'active_sessions' => 0
        ];
        
        $activeThreshold = time() - 300;  // 5分以内
        
        foreach ($this->storage as $data) {
            // デバイス別
            $deviceType = $data['device_info']['type'] ?? 'unknown';
            if (!isset($stats['by_device'][$deviceType])) {
                $stats['by_device'][$deviceType] = 0;
            }
            $stats['by_device'][$deviceType]++;
            
            // ユーザー別
            $userId = $data['user_id'];
            if (!isset($stats['by_user'][$userId])) {
                $stats['by_user'][$userId] = 0;
            }
            $stats['by_user'][$userId]++;
            
            // アクティブセッション
            if ($data['last_activity'] >= $activeThreshold) {
                $stats['active_sessions']++;
            }
        }
        
        return $stats;
    }
    
    /**
     * セッション一覧を表示
     */
    public function displaySessions($userId) {
        $sessions = $this->getUserSessions($userId);
        
        echo "=== User Sessions (User ID: {$userId}) ===\n\n";
        echo "Total Sessions: " . count($sessions) . "\n\n";
        
        foreach ($sessions as $index => $session) {
            echo "Session " . ($index + 1) . ":\n";
            echo "  ID: {$session['session_id']}\n";
            echo "  Device: {$session['device_type']} - {$session['device_name']}\n";
            echo "  Created: " . date('Y-m-d H:i:s', $session['created_at']) . "\n";
            echo "  Last Activity: " . date('Y-m-d H:i:s', $session['last_activity']) . "\n";
            echo "  Age: " . round($session['age'] / 60, 2) . " minutes\n";
            echo "  Current: " . ($session['is_current'] ? 'Yes' : 'No') . "\n";
            echo "\n";
        }
    }
}

// 使用例
echo "=== マルチデバイスセッション管理 ===\n";

$multiManager = new MultiDeviceSessionManager();
$userId = 123;

// デバイス1: デスクトップ
echo "\nデスクトップセッション作成:\n";
$desktop = $multiManager->createDeviceSession($userId, [
    'type' => 'desktop',
    'name' => 'Windows PC',
    'browser' => 'Chrome'
]);
echo "セッションID: {$desktop['session_id']}\n";

// デバイス2: モバイル
session_write_close();
echo "\nモバイルセッション作成:\n";
$mobile = $multiManager->createDeviceSession($userId, [
    'type' => 'mobile',
    'name' => 'iPhone 12',
    'browser' => 'Safari'
]);
echo "セッションID: {$mobile['session_id']}\n";

// デバイス3: タブレット
session_write_close();
echo "\nタブレットセッション作成:\n";
$tablet = $multiManager->createDeviceSession($userId, [
    'type' => 'tablet',
    'name' => 'iPad Pro',
    'browser' => 'Safari'
]);
echo "セッションID: {$tablet['session_id']}\n";

// ユーザーのすべてのセッションを表示
echo "\n";
$multiManager->displaySessions($userId);

// 統計情報
echo "=== セッション統計 ===\n";
$stats = $multiManager->getSessionStatistics();
echo "総セッション数: {$stats['total_sessions']}\n";
echo "アクティブセッション: {$stats['active_sessions']}\n";
echo "\nデバイス別:\n";
foreach ($stats['by_device'] as $device => $count) {
    echo "  {$device}: {$count}\n";
}

// 特定のセッションを終了
echo "\n=== モバイルセッションを終了 ===\n";
$terminated = $multiManager->terminateSession($mobile['session_id']);
echo "終了: " . ($terminated['success'] ? 'Yes' : 'No') . "\n";

// 他のすべてのセッションを終了
session_write_close();
session_id($desktop['session_id']);
session_start();

echo "\n=== 他のすべてのセッションを終了 ===\n";
$terminateOthers = $multiManager->terminateOtherSessions($userId);
echo "終了したセッション数: {$terminateOthers['terminated']}\n";

// 残りのセッション
echo "\n残りのセッション:\n";
$multiManager->displaySessions($userId);

例3: セッションID追跡システム

class SessionIdTracker {
    private $trackingFile;
    private $tracks = [];
    
    /**
     * トラッカーを初期化
     */
    public function __construct($trackingFile = '/tmp/session_id_tracking.json') {
        $this->trackingFile = $trackingFile;
        $this->loadTracks();
    }
    
    /**
     * トラッキングデータを読み込み
     */
    private function loadTracks() {
        if (file_exists($this->trackingFile)) {
            $this->tracks = json_decode(file_get_contents($this->trackingFile), true) ?? [];
        }
    }
    
    /**
     * トラッキングデータを保存
     */
    private function saveTracks() {
        file_put_contents($this->trackingFile, json_encode($this->tracks, JSON_PRETTY_PRINT));
    }
    
    /**
     * セッションIDを追跡開始
     */
    public function startTracking($label = null) {
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
        
        $sessionId = session_id();
        
        if (!isset($this->tracks[$sessionId])) {
            $this->tracks[$sessionId] = [
                'session_id' => $sessionId,
                'first_seen' => time(),
                'labels' => [],
                'events' => [],
                'page_views' => 0,
                'user_agents' => [],
                'ip_addresses' => []
            ];
        }
        
        if ($label) {
            $this->tracks[$sessionId]['labels'][] = $label;
        }
        
        $this->trackEvent('tracking_started', ['label' => $label]);
        
        return $this->tracks[$sessionId];
    }
    
    /**
     * イベントを記録
     */
    public function trackEvent($eventType, $data = []) {
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
        
        $sessionId = session_id();
        
        if (!isset($this->tracks[$sessionId])) {
            $this->startTracking();
        }
        
        $event = [
            'type' => $eventType,
            'timestamp' => time(),
            'data' => $data,
            'url' => $_SERVER['REQUEST_URI'] ?? 'unknown',
            'referer' => $_SERVER['HTTP_REFERER'] ?? null
        ];
        
        $this->tracks[$sessionId]['events'][] = $event;
        $this->tracks[$sessionId]['last_activity'] = time();
        
        // ページビューカウント
        if ($eventType === 'page_view') {
            $this->tracks[$sessionId]['page_views']++;
        }
        
        // User Agent記録
        $userAgent = $_SERVER['HTTP_USER_AGENT'] ?? 'unknown';
        if (!in_array($userAgent, $this->tracks[$sessionId]['user_agents'])) {
            $this->tracks[$sessionId]['user_agents'][] = $userAgent;
        }
        
        // IPアドレス記録
        $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
        if (!in_array($ip, $this->tracks[$sessionId]['ip_addresses'])) {
            $this->tracks[$sessionId]['ip_addresses'][] = $ip;
        }
        
        $this->saveTracks();
        
        return $event;
    }
    
    /**
     * ページビューを記録
     */
    public function trackPageView($page = null) {
        $page = $page ?? ($_SERVER['REQUEST_URI'] ?? 'unknown');
        
        return $this->trackEvent('page_view', [
            'page' => $page,
            'method' => $_SERVER['REQUEST_METHOD'] ?? 'unknown'
        ]);
    }
    
    /**
     * ユーザーアクションを記録
     */
    public function trackAction($action, $details = []) {
        return $this->trackEvent('user_action', [
            'action' => $action,
            'details' => $details
        ]);
    }
    
    /**
     * セッション情報を取得
     */
    public function getSessionInfo($sessionId = null) {
        if ($sessionId === null) {
            if (session_status() !== PHP_SESSION_ACTIVE) {
                session_start();
            }
            $sessionId = session_id();
        }
        
        if (!isset($this->tracks[$sessionId])) {
            return null;
        }
        
        $track = $this->tracks[$sessionId];
        $duration = isset($track['last_activity']) ? 
            $track['last_activity'] - $track['first_seen'] : 0;
        
        return [
            'session_id' => $sessionId,
            'first_seen' => $track['first_seen'],
            'last_activity' => $track['last_activity'] ?? $track['first_seen'],
            'duration' => $duration,
            'page_views' => $track['page_views'],
            'total_events' => count($track['events']),
            'labels' => $track['labels'],
            'unique_ips' => count($track['ip_addresses']),
            'unique_user_agents' => count($track['user_agents'])
        ];
    }
    
    /**
     * セッションのタイムラインを取得
     */
    public function getTimeline($sessionId = null) {
        if ($sessionId === null) {
            if (session_status() !== PHP_SESSION_ACTIVE) {
                session_start();
            }
            $sessionId = session_id();
        }
        
        if (!isset($this->tracks[$sessionId])) {
            return [];
        }
        
        return $this->tracks[$sessionId]['events'];
    }
    
    /**
     * すべてのセッションを取得
     */
    public function getAllSessions($limit = null) {
        $sessions = [];
        
        foreach ($this->tracks as $sessionId => $track) {
            $sessions[] = $this->getSessionInfo($sessionId);
        }
        
        // 最新順にソート
        usort($sessions, function($a, $b) {
            return $b['last_activity'] - $a['last_activity'];
        });
        
        if ($limit !== null) {
            $sessions = array_slice($sessions, 0, $limit);
        }
        
        return $sessions;
    }
    
    /**
     * 統計情報を取得
     */
    public function getStatistics() {
        $totalSessions = count($this->tracks);
        $totalPageViews = 0;
        $totalEvents = 0;
        $activeThreshold = time() - 1800;  // 30分
        $activeSessions = 0;
        
        foreach ($this->tracks as $track) {
            $totalPageViews += $track['page_views'];
            $totalEvents += count($track['events']);
            
            if (isset($track['last_activity']) && $track['last_activity'] >= $activeThreshold) {
                $activeSessions++;
            }
        }
        
        return [
            'total_sessions' => $totalSessions,
            'active_sessions' => $activeSessions,
            'total_page_views' => $totalPageViews,
            'total_events' => $totalEvents,
            'average_page_views' => $totalSessions > 0 ? 
                round($totalPageViews / $totalSessions, 2) : 0,
            'average_events' => $totalSessions > 0 ? 
                round($totalEvents / $totalSessions, 2) : 0
        ];
    }
    
    /**
     * レポートを生成
     */
    public function generateReport($sessionId = null) {
        if ($sessionId === null) {
            // 全体レポート
            $stats = $this->getStatistics();
            
            $report = "=== Session Tracking Report ===\n\n";
            $report .= "Total Sessions: {$stats['total_sessions']}\n";
            $report .= "Active Sessions: {$stats['active_sessions']}\n";
            $report .= "Total Page Views: {$stats['total_page_views']}\n";
            $report .= "Total Events: {$stats['total_events']}\n";
            $report .= "Average Page Views: {$stats['average_page_views']}\n";
            $report .= "Average Events: {$stats['average_events']}\n\n";
            
            $report .= "Recent Sessions:\n";
            foreach ($this->getAllSessions(5) as $session) {
                $report .= "  {$session['session_id']}: ";
                $report .= "{$session['page_views']} views, ";
                $report .= date('Y-m-d H:i:s', $session['last_activity']) . "\n";
            }
        } else {
            // 個別セッションレポート
            $info = $this->getSessionInfo($sessionId);
            
            if ($info === null) {
                return "Session not found: {$sessionId}";
            }
            
            $report = "=== Session Report ===\n\n";
            $report .= "Session ID: {$info['session_id']}\n";
            $report .= "First Seen: " . date('Y-m-d H:i:s', $info['first_seen']) . "\n";
            $report .= "Last Activity: " . date('Y-m-d H:i:s', $info['last_activity']) . "\n";
            $report .= "Duration: " . round($info['duration'] / 60, 2) . " minutes\n";
            $report .= "Page Views: {$info['page_views']}\n";
            $report .= "Total Events: {$info['total_events']}\n";
            $report .= "Unique IPs: {$info['unique_ips']}\n";
            
            if (!empty($info['labels'])) {
                $report .= "Labels: " . implode(', ', $info['labels']) . "\n";
            }
            
            $report .= "\nTimeline:\n";
            foreach ($this->getTimeline($sessionId) as $event) {
                $report .= "  [" . date('H:i:s', $event['timestamp']) . "] ";
                $report .= "{$event['type']}\n";
            }
        }
        
        return $report;
    }
}

// 使用例
echo "=== セッションID追跡システム ===\n";

$tracker = new SessionIdTracker('/tmp/session_tracking_test.json');

// 追跡開始
session_start();
$tracker->startTracking('test_session');
echo "追跡開始: " . session_id() . "\n";

// ページビューを記録
$tracker->trackPageView('/home');
$tracker->trackPageView('/products');
$tracker->trackPageView('/checkout');

// ユーザーアクションを記録
$tracker->trackAction('add_to_cart', ['product_id' => 123, 'quantity' => 2]);
$tracker->trackAction('checkout_started');
$tracker->trackAction('payment_completed', ['amount' => 5000]);

// セッション情報
echo "\nセッション情報:\n";
$info = $tracker->getSessionInfo();
echo "ページビュー数: {$info['page_views']}\n";
echo "総イベント数: {$info['total_events']}\n";
echo "セッション時間: " . round($info['duration'] / 60, 2) . "分\n";

// タイムライン
echo "\nタイムライン:\n";
foreach ($tracker->getTimeline() as $index => $event) {
    echo ($index + 1) . ". [" . date('H:i:s', $event['timestamp']) . "] {$event['type']}\n";
}

// 統計情報
echo "\n統計情報:\n";
$stats = $tracker->getStatistics();
echo "総セッション数: {$stats['total_sessions']}\n";
echo "総ページビュー数: {$stats['total_page_views']}\n";
echo "平均ページビュー: {$stats['average_page_views']}\n";

// レポート生成
echo "\n";
echo $tracker->generateReport(session_id());

続きは次のレスポンスで提供します。

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