[PHP]session_gc関数を完全解説!セッションガベージコレクションを手動実行する方法

PHP

こんにちは!今回は、PHPの標準関数であるsession_gc()について詳しく解説していきます。期限切れのセッションデータを削除できる、セッション管理の最適化に重要な関数です!

session_gc関数とは?

session_gc()関数は、期限切れのセッションデータをガベージコレクション(削除)する関数です。

通常はPHPが自動的に実行しますが、この関数を使うことで手動でセッションストレージをクリーンアップできます。PHP 7.1.0以降で使用可能です!

基本的な構文

session_gc(): int|false
  • 引数: なし
  • 戻り値: 削除されたセッション数、失敗時はfalse

重要な注意点

// PHP 7.1.0以降で使用可能
if (function_exists('session_gc')) {
    $deleted = session_gc();
    echo "削除されたセッション: {$deleted}件\n";
} else {
    echo "session_gc()は使用できません\n";
}

// session.gc_maxlifetimeに基づいて削除
// デフォルトは1440秒(24分)
echo "GC最大有効期限: " . ini_get('session.gc_maxlifetime') . "秒\n";

// セッションが開始されている必要はない
// session_gc()はセッション開始前でも実行可能
$deleted = session_gc();

// 自動ガベージコレクションの設定
// session.gc_probability / session.gc_divisor = 実行確率
echo "GC確率: " . ini_get('session.gc_probability') . "/" . ini_get('session.gc_divisor') . "\n";
// デフォルト: 1/100 = 1%の確率で実行

// ファイルベースセッションでのみ動作
// カスタムセッションハンドラーでは動作が異なる

ガベージコレクションの仕組み

// セッションの自動ガベージコレクション

// 1. session.gc_maxlifetime (デフォルト1440秒)
//    この時間を超えたセッションファイルが削除対象
ini_set('session.gc_maxlifetime', 3600);  // 1時間

// 2. session.gc_probability (デフォルト1)
//    ガベージコレクション実行の確率(分子)
ini_set('session.gc_probability', 1);

// 3. session.gc_divisor (デフォルト100)
//    ガベージコレクション実行の確率(分母)
ini_set('session.gc_divisor', 100);

// 実行確率 = gc_probability / gc_divisor
// デフォルト = 1/100 = 1%

// session_start()時に確率的に実行される
session_start();  // 1%の確率でGCが実行される

// 手動で確実に実行
$deleted = session_gc();
echo "削除: {$deleted}件\n";

基本的な使用例

シンプルなガベージコレクション

// 期限切れセッションを削除
echo "ガベージコレクション実行前\n";

// 削除を実行
$deleted = session_gc();

echo "削除されたセッション: {$deleted}件\n";

GC設定の確認と変更

// 現在のGC設定を確認
echo "=== GC設定 ===\n";
echo "gc_maxlifetime: " . ini_get('session.gc_maxlifetime') . "秒\n";
echo "gc_probability: " . ini_get('session.gc_probability') . "\n";
echo "gc_divisor: " . ini_get('session.gc_divisor') . "\n";
echo "実行確率: " . (ini_get('session.gc_probability') / ini_get('session.gc_divisor') * 100) . "%\n";

// GC設定を変更
ini_set('session.gc_maxlifetime', 7200);  // 2時間
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 10);  // 10%の確率

echo "\n=== 変更後 ===\n";
echo "gc_maxlifetime: " . ini_get('session.gc_maxlifetime') . "秒\n";
echo "実行確率: " . (ini_get('session.gc_probability') / ini_get('session.gc_divisor') * 100) . "%\n";

// GCを実行
$deleted = session_gc();
echo "\n削除されたセッション: {$deleted}件\n";

セッションファイルの確認

// セッションファイルの状態を確認
$sessionPath = session_save_path();
if (empty($sessionPath)) {
    $sessionPath = sys_get_temp_dir();
}

echo "セッションパス: {$sessionPath}\n";

// セッションファイル数をカウント
$files = glob($sessionPath . '/sess_*');
echo "GC実行前のセッションファイル数: " . count($files) . "\n";

// ガベージコレクション実行
$deleted = session_gc();
echo "削除されたセッション: {$deleted}件\n";

// 実行後のファイル数
$files = glob($sessionPath . '/sess_*');
echo "GC実行後のセッションファイル数: " . count($files) . "\n";

定期的なクリーンアップ

// 毎回確実にGCを実行したい場合
function ensureSessionCleanup() {
    // GC確率を100%に設定
    ini_set('session.gc_probability', 1);
    ini_set('session.gc_divisor', 1);
    
    session_start();
    
    // さらに手動でも実行
    $deleted = session_gc();
    
    return $deleted;
}

$deleted = ensureSessionCleanup();
echo "削除されたセッション: {$deleted}件\n";

実践的な使用例

例1: セッションクリーンアップシステム

class SessionCleanupManager {
    private $gcMaxLifetime;
    private $sessionPath;
    
    /**
     * クリーンアップマネージャーを初期化
     */
    public function __construct($gcMaxLifetime = null) {
        $this->gcMaxLifetime = $gcMaxLifetime ?? ini_get('session.gc_maxlifetime');
        $this->sessionPath = session_save_path();
        
        if (empty($this->sessionPath)) {
            $this->sessionPath = sys_get_temp_dir();
        }
    }
    
    /**
     * ガベージコレクションを実行
     */
    public function runGarbageCollection() {
        // 実行前の状態を記録
        $beforeStats = $this->getSessionStats();
        
        // GC実行
        $startTime = microtime(true);
        $deleted = session_gc();
        $duration = microtime(true) - $startTime;
        
        // 実行後の状態を記録
        $afterStats = $this->getSessionStats();
        
        return [
            'deleted_count' => $deleted,
            'duration' => $duration,
            'before' => $beforeStats,
            'after' => $afterStats,
            'freed_space' => $beforeStats['total_size'] - $afterStats['total_size']
        ];
    }
    
    /**
     * セッション統計情報を取得
     */
    public function getSessionStats() {
        $files = glob($this->sessionPath . '/sess_*');
        $totalSize = 0;
        $oldestTime = time();
        $newestTime = 0;
        $expiredCount = 0;
        $cutoff = time() - $this->gcMaxLifetime;
        
        foreach ($files as $file) {
            $size = filesize($file);
            $mtime = filemtime($file);
            
            $totalSize += $size;
            
            if ($mtime < $oldestTime) {
                $oldestTime = $mtime;
            }
            if ($mtime > $newestTime) {
                $newestTime = $mtime;
            }
            
            if ($mtime < $cutoff) {
                $expiredCount++;
            }
        }
        
        return [
            'total_sessions' => count($files),
            'expired_sessions' => $expiredCount,
            'active_sessions' => count($files) - $expiredCount,
            'total_size' => $totalSize,
            'total_size_mb' => round($totalSize / 1024 / 1024, 2),
            'oldest_session_age' => time() - $oldestTime,
            'newest_session_age' => time() - $newestTime,
            'gc_maxlifetime' => $this->gcMaxLifetime
        ];
    }
    
    /**
     * クリーンアップレポートを生成
     */
    public function generateCleanupReport() {
        $stats = $this->getSessionStats();
        
        $report = "=== Session Cleanup Report ===\n";
        $report .= "Total Sessions: {$stats['total_sessions']}\n";
        $report .= "Active Sessions: {$stats['active_sessions']}\n";
        $report .= "Expired Sessions: {$stats['expired_sessions']}\n";
        $report .= "Total Size: {$stats['total_size_mb']} MB\n";
        $report .= "GC Max Lifetime: {$stats['gc_maxlifetime']} seconds\n";
        $report .= "Oldest Session Age: {$stats['oldest_session_age']} seconds\n";
        
        return $report;
    }
    
    /**
     * 条件付きクリーンアップ
     */
    public function cleanupIfNeeded($threshold = 100) {
        $stats = $this->getSessionStats();
        
        if ($stats['expired_sessions'] >= $threshold) {
            return $this->runGarbageCollection();
        }
        
        return [
            'cleaned' => false,
            'reason' => 'Threshold not reached',
            'expired_sessions' => $stats['expired_sessions'],
            'threshold' => $threshold
        ];
    }
    
    /**
     * スケジュールされたクリーンアップ
     */
    public function scheduledCleanup($interval = 3600) {
        $lastRunFile = $this->sessionPath . '/.last_gc_run';
        
        // 最後の実行時刻を確認
        if (file_exists($lastRunFile)) {
            $lastRun = (int)file_get_contents($lastRunFile);
            $timeSinceLastRun = time() - $lastRun;
            
            if ($timeSinceLastRun < $interval) {
                return [
                    'cleaned' => false,
                    'reason' => 'Too soon since last run',
                    'time_since_last_run' => $timeSinceLastRun,
                    'interval' => $interval
                ];
            }
        }
        
        // クリーンアップ実行
        $result = $this->runGarbageCollection();
        
        // 実行時刻を記録
        file_put_contents($lastRunFile, time());
        
        $result['scheduled'] = true;
        return $result;
    }
    
    /**
     * アグレッシブクリーンアップ(短い有効期限で実行)
     */
    public function aggressiveCleanup($customLifetime = 600) {
        // 一時的に有効期限を変更
        $originalLifetime = ini_get('session.gc_maxlifetime');
        ini_set('session.gc_maxlifetime', $customLifetime);
        
        // GC実行
        $deleted = session_gc();
        
        // 元に戻す
        ini_set('session.gc_maxlifetime', $originalLifetime);
        
        return [
            'deleted_count' => $deleted,
            'custom_lifetime' => $customLifetime,
            'original_lifetime' => $originalLifetime
        ];
    }
    
    /**
     * クリーンアップ履歴を記録
     */
    public function logCleanup($result) {
        $logFile = $this->sessionPath . '/gc_log.json';
        $logs = [];
        
        if (file_exists($logFile)) {
            $logs = json_decode(file_get_contents($logFile), true) ?? [];
        }
        
        $logs[] = [
            'timestamp' => time(),
            'deleted_count' => $result['deleted_count'],
            'duration' => $result['duration'] ?? 0,
            'freed_space' => $result['freed_space'] ?? 0
        ];
        
        // 最新100件のみ保持
        if (count($logs) > 100) {
            $logs = array_slice($logs, -100);
        }
        
        file_put_contents($logFile, json_encode($logs));
    }
    
    /**
     * クリーンアップ統計を取得
     */
    public function getCleanupStats() {
        $logFile = $this->sessionPath . '/gc_log.json';
        
        if (!file_exists($logFile)) {
            return ['error' => 'No cleanup logs available'];
        }
        
        $logs = json_decode(file_get_contents($logFile), true);
        
        $totalDeleted = 0;
        $totalDuration = 0;
        $totalFreed = 0;
        
        foreach ($logs as $log) {
            $totalDeleted += $log['deleted_count'];
            $totalDuration += $log['duration'] ?? 0;
            $totalFreed += $log['freed_space'] ?? 0;
        }
        
        return [
            'total_runs' => count($logs),
            'total_deleted' => $totalDeleted,
            'average_deleted' => round($totalDeleted / count($logs), 2),
            'average_duration' => round($totalDuration / count($logs), 4),
            'total_freed_mb' => round($totalFreed / 1024 / 1024, 2),
            'last_run' => $logs[count($logs) - 1]['timestamp'] ?? null
        ];
    }
}

// 使用例
echo "=== セッションクリーンアップシステム ===\n";

$cleanup = new SessionCleanupManager();

// 現在の統計
echo $cleanup->generateCleanupReport();

// ガベージコレクション実行
echo "\n=== GC実行 ===\n";
$result = $cleanup->runGarbageCollection();
echo "削除されたセッション: {$result['deleted_count']}件\n";
echo "実行時間: " . round($result['duration'], 4) . "秒\n";
echo "解放された容量: " . round($result['freed_space'] / 1024, 2) . " KB\n";

// ログに記録
$cleanup->logCleanup($result);

// 条件付きクリーンアップ
echo "\n=== 条件付きクリーンアップ ===\n";
$conditional = $cleanup->cleanupIfNeeded(5);
if ($conditional['cleaned']) {
    echo "クリーンアップ実行: {$conditional['deleted_count']}件削除\n";
} else {
    echo "クリーンアップ不要: {$conditional['reason']}\n";
    echo "期限切れセッション: {$conditional['expired_sessions']}件\n";
}

// スケジュールされたクリーンアップ
echo "\n=== スケジュールクリーンアップ ===\n";
$scheduled = $cleanup->scheduledCleanup(60);
if ($scheduled['cleaned']) {
    echo "実行: {$scheduled['deleted_count']}件削除\n";
} else {
    echo "スキップ: {$scheduled['reason']}\n";
}

// クリーンアップ統計
echo "\n=== クリーンアップ統計 ===\n";
$stats = $cleanup->getCleanupStats();
if (!isset($stats['error'])) {
    echo "総実行回数: {$stats['total_runs']}\n";
    echo "総削除数: {$stats['total_deleted']}\n";
    echo "平均削除数: {$stats['average_deleted']}\n";
    echo "解放容量合計: {$stats['total_freed_mb']} MB\n";
}

例2: カスタムガベージコレクター

class CustomGarbageCollector {
    private $rules = [];
    
    /**
     * カスタムルールを追加
     */
    public function addRule($name, $callback) {
        $this->rules[$name] = $callback;
    }
    
    /**
     * カスタムガベージコレクションを実行
     */
    public function collect() {
        $sessionPath = session_save_path();
        if (empty($sessionPath)) {
            $sessionPath = sys_get_temp_dir();
        }
        
        $files = glob($sessionPath . '/sess_*');
        $deleted = 0;
        $results = [];
        
        foreach ($files as $file) {
            $sessionId = substr(basename($file), 5);  // 'sess_'を除去
            
            // セッションデータを読み込み
            $data = file_get_contents($file);
            $mtime = filemtime($file);
            
            // 各ルールを適用
            foreach ($this->rules as $name => $callback) {
                if ($callback($file, $data, $mtime, $sessionId)) {
                    if (unlink($file)) {
                        $deleted++;
                        $results[] = [
                            'session_id' => $sessionId,
                            'rule' => $name,
                            'deleted' => true
                        ];
                    }
                    break;  // 削除したらループを抜ける
                }
            }
        }
        
        return [
            'deleted_count' => $deleted,
            'details' => $results
        ];
    }
    
    /**
     * 標準的なルールセットを適用
     */
    public function applyStandardRules() {
        // ルール1: 古いセッション(1時間以上)
        $this->addRule('old_sessions', function($file, $data, $mtime, $sessionId) {
            return (time() - $mtime) > 3600;
        });
        
        // ルール2: 空のセッション
        $this->addRule('empty_sessions', function($file, $data, $mtime, $sessionId) {
            return empty($data) || strlen($data) < 10;
        });
        
        // ルール3: 巨大なセッション(1MB以上)
        $this->addRule('large_sessions', function($file, $data, $mtime, $sessionId) {
            return strlen($data) > 1048576;
        });
    }
    
    /**
     * セキュリティルールを適用
     */
    public function applySecurityRules() {
        // ルール: 疑わしいデータを含むセッション
        $this->addRule('suspicious_data', function($file, $data, $mtime, $sessionId) {
            // 例: 特定のパターンを検出
            $suspicious = [
                'eval(',
                'base64_decode(',
                'system(',
                'exec('
            ];
            
            foreach ($suspicious as $pattern) {
                if (strpos($data, $pattern) !== false) {
                    return true;
                }
            }
            
            return false;
        });
        
        // ルール: 異常に古いセッション(24時間以上)
        $this->addRule('very_old_sessions', function($file, $data, $mtime, $sessionId) {
            return (time() - $mtime) > 86400;
        });
    }
    
    /**
     * パフォーマンスルールを適用
     */
    public function applyPerformanceRules() {
        // ルール: 非アクティブなセッション(30分以上)
        $this->addRule('inactive_sessions', function($file, $data, $mtime, $sessionId) {
            return (time() - $mtime) > 1800;
        });
        
        // ルール: 重複セッション(同じユーザーの古いセッション)
        $this->addRule('duplicate_sessions', function($file, $data, $mtime, $sessionId) {
            // 簡易実装: セッションデータからuser_idを抽出して判定
            // 実際の実装ではもっと複雑な判定が必要
            return false;  // プレースホルダー
        });
    }
    
    /**
     * ドライランモード(削除せずに候補を表示)
     */
    public function dryRun() {
        $sessionPath = session_save_path();
        if (empty($sessionPath)) {
            $sessionPath = sys_get_temp_dir();
        }
        
        $files = glob($sessionPath . '/sess_*');
        $candidates = [];
        
        foreach ($files as $file) {
            $sessionId = substr(basename($file), 5);
            $data = file_get_contents($file);
            $mtime = filemtime($file);
            
            foreach ($this->rules as $name => $callback) {
                if ($callback($file, $data, $mtime, $sessionId)) {
                    $candidates[] = [
                        'session_id' => $sessionId,
                        'file' => $file,
                        'rule' => $name,
                        'size' => filesize($file),
                        'age' => time() - $mtime
                    ];
                    break;
                }
            }
        }
        
        return $candidates;
    }
    
    /**
     * 選択的削除(特定のルールのみ)
     */
    public function collectByRule($ruleName) {
        if (!isset($this->rules[$ruleName])) {
            throw new Exception("Rule not found: {$ruleName}");
        }
        
        $sessionPath = session_save_path();
        if (empty($sessionPath)) {
            $sessionPath = sys_get_temp_dir();
        }
        
        $files = glob($sessionPath . '/sess_*');
        $deleted = 0;
        $callback = $this->rules[$ruleName];
        
        foreach ($files as $file) {
            $sessionId = substr(basename($file), 5);
            $data = file_get_contents($file);
            $mtime = filemtime($file);
            
            if ($callback($file, $data, $mtime, $sessionId)) {
                if (unlink($file)) {
                    $deleted++;
                }
            }
        }
        
        return [
            'rule' => $ruleName,
            'deleted_count' => $deleted
        ];
    }
}

// 使用例
echo "=== カスタムガベージコレクター ===\n";

$collector = new CustomGarbageCollector();

// 標準ルールを適用
$collector->applyStandardRules();
$collector->applySecurityRules();

// ドライラン(削除候補を確認)
echo "削除候補:\n";
$candidates = $collector->dryRun();
foreach (array_slice($candidates, 0, 5) as $candidate) {
    echo "  {$candidate['session_id']} - {$candidate['rule']} (age: {$candidate['age']}s, size: {$candidate['size']}bytes)\n";
}
echo "総候補数: " . count($candidates) . "\n";

// 実際に削除
echo "\nカスタムGC実行:\n";
$result = $collector->collect();
echo "削除されたセッション: {$result['deleted_count']}件\n";

// 特定のルールのみで削除
echo "\n古いセッションのみ削除:\n";
$oldResult = $collector->collectByRule('old_sessions');
echo "削除: {$oldResult['deleted_count']}件\n";

例3: セッションストレージ最適化システム

class SessionStorageOptimizer {
    private $sessionPath;
    private $targetSize;  // 目標サイズ(バイト)
    
    /**
     * 最適化システムを初期化
     */
    public function __construct($targetSize = 10485760) {  // デフォルト10MB
        $this->sessionPath = session_save_path();
        if (empty($this->sessionPath)) {
            $this->sessionPath = sys_get_temp_dir();
        }
        $this->targetSize = $targetSize;
    }
    
    /**
     * ストレージを最適化
     */
    public function optimize() {
        $beforeStats = $this->getStorageStats();
        
        if ($beforeStats['total_size'] <= $this->targetSize) {
            return [
                'optimized' => false,
                'reason' => 'Already below target size',
                'current_size' => $beforeStats['total_size'],
                'target_size' => $this->targetSize
            ];
        }
        
        // 最適化戦略を実行
        $results = [];
        
        // 1. 標準GCを実行
        $gcResult = $this->runStandardGC();
        $results['standard_gc'] = $gcResult;
        
        // まだ目標を超えている場合
        $currentStats = $this->getStorageStats();
        if ($currentStats['total_size'] > $this->targetSize) {
            // 2. 古いセッションを積極的に削除
            $aggressiveResult = $this->aggressiveCleanup();
            $results['aggressive_cleanup'] = $aggressiveResult;
        }
        
        // まだ目標を超えている場合
        $currentStats = $this->getStorageStats();
        if ($currentStats['total_size'] > $this->targetSize) {
            // 3. 最も古いセッションから順に削除
            $priorityResult = $this->priorityCleanup();
            $results['priority_cleanup'] = $priorityResult;
        }
        
        $afterStats = $this->getStorageStats();
        
        return [
            'optimized' => true,
            'before' => $beforeStats,
            'after' => $afterStats,
            'freed_space' => $beforeStats['total_size'] - $afterStats['total_size'],
            'results' => $results
        ];
    }
    
    /**
     * ストレージ統計を取得
     */
    private function getStorageStats() {
        $files = glob($this->sessionPath . '/sess_*');
        $totalSize = 0;
        
        foreach ($files as $file) {
            $totalSize += filesize($file);
        }
        
        return [
            'total_sessions' => count($files),
            'total_size' => $totalSize,
            'total_size_mb' => round($totalSize / 1024 / 1024, 2),
            'average_size' => count($files) > 0 ? round($totalSize / count($files), 2) : 0
        ];
    }
    
    /**
     * 標準GCを実行
     */
    private function runStandardGC() {
        $deleted = session_gc();
        
        return [
            'method' => 'standard_gc',
            'deleted' => $deleted
        ];
    }
    
    /**
     * アグレッシブクリーンアップ
     */
    private function aggressiveCleanup() {
        // 有効期限を一時的に短く設定
        $originalLifetime = ini_get('session.gc_maxlifetime');
        ini_set('session.gc_maxlifetime', 600);  // 10分
        
        $deleted = session_gc();
        
        ini_set('session.gc_maxlifetime', $originalLifetime);
        
        return [
            'method' => 'aggressive',
            'deleted' => $deleted,
            'lifetime_used' => 600
        ];
    }
    
    /**
     * 優先度ベースのクリーンアップ
     */
    private function priorityCleanup() {
        $files = glob($this->sessionPath . '/sess_*');
        $deleted = 0;
        
        // ファイルを古い順にソート
        usort($files, function($a, $b) {
            return filemtime($a) - filemtime($b);
        });
        
        // 目標サイズに達するまで削除
        $currentSize = $this->getStorageStats()['total_size'];
        
        foreach ($files as $file) {
            if ($currentSize <= $this->targetSize) {
                break;
            }
            
            $fileSize = filesize($file);
            if (unlink($file)) {
                $deleted++;
                $currentSize -= $fileSize;
            }
        }
        
        return [
            'method' => 'priority',
            'deleted' => $deleted
        ];
    }
    
    /**
     * 自動最適化(定期実行用)
     */
    public function autoOptimize($checkInterval = 3600) {
        $lastCheckFile = $this->sessionPath . '/.last_optimization';
        
        // 最後のチェック時刻を確認
        if (file_exists($lastCheckFile)) {
            $lastCheck = (int)file_get_contents($lastCheckFile);
            if (time() - $lastCheck < $checkInterval) {
                return [
                    'checked' => false,
                    'reason' => 'Too soon since last check'
                ];
            }
        }
        
        // 最適化を実行
        $result = $this->optimize();
        
        // チェック時刻を記録
        file_put_contents($lastCheckFile, time());
        
        $result['auto'] = true;
        return $result;
    }
    
    /**
     * 圧縮可能なセッションを検出
     */
    public function findCompressible() {
        $files = glob($this->sessionPath . '/sess_*');
        $compressible = [];
        
        foreach ($files as $file) {
            $size = filesize($file);
            if ($size > 1024) {  // 1KB以上
                $data = file_get_contents($file);
                $compressed = gzcompress($data);
                $compressedSize = strlen($compressed);
                $ratio = $compressedSize / $size;
                
                if ($ratio < 0.7) {  // 30%以上圧縮可能
                    $compressible[] = [
                        'file' => basename($file),
                        'original_size' => $size,
                        'compressed_size' => $compressedSize,
                        'ratio' => round($ratio * 100, 2) . '%',
                        'savings' => $size - $compressedSize
                    ];
                }
            }
        }
        
        return $compressible;
    }
    
    /**
     * 最適化レポートを生成
     */
    public function generateOptimizationReport() {
        $stats = $this->getStorageStats();
        $compressible = $this->findCompressible();
        
        $totalSavings = array_sum(array_column($compressible, 'savings'));
        
        $report = "=== Storage Optimization Report ===\n";
        $report .= "Current Size: {$stats['total_size_mb']} MB\n";
        $report .= "Target Size: " . round($this->targetSize / 1024 / 1024, 2) . " MB\n";
        $report .= "Total Sessions: {$stats['total_sessions']}\n";
        $report .= "Average Session Size: {$stats['average_size']} bytes\n";
        $report .= "\nCompression Potential:\n";
        $report .= "Compressible Sessions: " . count($compressible) . "\n";
        $report .= "Potential Savings: " . round($totalSavings / 1024, 2) . " KB\n";
        
        if ($stats['total_size'] > $this->targetSize) {
            $report .= "\nRECOMMENDATION: Run optimization\n";
            $excess = $stats['total_size'] - $this->targetSize;
            $report .= "Excess Size: " . round($excess / 1024, 2) . " KB\n";
        } else {
            $report .= "\nSTATUS: Storage within target\n";
        }
        
        return $report;
    }
}

// 使用例
echo "=== セッションストレージ最適化 ===\n";

$optimizer = new SessionStorageOptimizer(5242880);  // 5MB目標

// 最適化レポート
echo $optimizer->generateOptimizationReport();

// 最適化実行
echo "\n=== 最適化実行 ===\n";
$result = $optimizer->optimize();
if ($result['optimized']) {
    echo "最適化完了\n";
    echo "削除前: {$result['before']['total_size_mb']} MB\n";
    echo "削除後: {$result['after']['total_size_mb']} MB\n";
    echo "解放容量: " . round($result['freed_space'] / 1024 / 1024, 2) . " MB\n";
} else {
    echo "最適化不要: {$result['reason']}\n";
}

// 圧縮可能なセッション
echo "\n=== 圧縮可能なセッション ===\n";
$compressible = $optimizer->findCompressible();
foreach (array_slice($compressible, 0, 5) as $item) {
    echo "{$item['file']}: {$item['original_size']}→{$item['compressed_size']} bytes ({$item['ratio']})\n";
}

例4: 定期クリーンアップスケジューラー

class SessionCleanupScheduler {
    private $schedules = [];
    private $logFile;
    
    /**
     * スケジューラーを初期化
     */
    public function __construct($logFile = '/tmp/session_cleanup_schedule.log') {
        $this->logFile = $logFile;
    }
    
    /**
     * スケジュールを追加
     */
    public function addSchedule($name, $interval, $callback) {
        $this->schedules[$name] = [
            'interval' => $interval,
            'callback' => $callback,
            'last_run' => 0
        ];
    }
    
    /**
     * スケジュールされたタスクを実行
     */
    public function run() {
        $executed = [];
        $currentTime = time();
        
        foreach ($this->schedules as $name => $schedule) {
            // インターバルチェック
            if ($currentTime - $schedule['last_run'] >= $schedule['interval']) {
                // タスクを実行
                $startTime = microtime(true);
                $result = call_user_func($schedule['callback']);
                $duration = microtime(true) - $startTime;
                
                // 実行時刻を更新
                $this->schedules[$name]['last_run'] = $currentTime;
                
                $executed[] = [
                    'name' => $name,
                    'result' => $result,
                    'duration' => $duration,
                    'timestamp' => $currentTime
                ];
                
                // ログに記録
                $this->log($name, $result, $duration);
            }
        }
        
        return $executed;
    }
    
    /**
     * ログに記録
     */
    private function log($name, $result, $duration) {
        $logEntry = sprintf(
            "[%s] %s: %s (%.4fs)\n",
            date('Y-m-d H:i:s'),
            $name,
            json_encode($result),
            $duration
        );
        
        file_put_contents($this->logFile, $logEntry, FILE_APPEND);
    }
    
    /**
     * 標準的なスケジュールを設定
     */
    public function setupStandardSchedules() {
        // 毎時間: 標準GC
        $this->addSchedule('hourly_gc', 3600, function() {
            return ['deleted' => session_gc()];
        });
        
        // 毎日: アグレッシブクリーンアップ
        $this->addSchedule('daily_aggressive', 86400, function() {
            $original = ini_get('session.gc_maxlifetime');
            ini_set('session.gc_maxlifetime', 3600);
            $deleted = session_gc();
            ini_set('session.gc_maxlifetime', $original);
            return ['deleted' => $deleted, 'lifetime' => 3600];
        });
        
        // 毎週: 完全クリーンアップ
        $this->addSchedule('weekly_full', 604800, function() {
            $original = ini_get('session.gc_maxlifetime');
            ini_set('session.gc_maxlifetime', 0);  // すべて削除
            $deleted = session_gc();
            ini_set('session.gc_maxlifetime', $original);
            return ['deleted' => $deleted, 'type' => 'full'];
        });
    }
    
    /**
     * 次回実行時刻を取得
     */
    public function getNextRun() {
        $nextRuns = [];
        $currentTime = time();
        
        foreach ($this->schedules as $name => $schedule) {
            $timeSinceLastRun = $currentTime - $schedule['last_run'];
            $timeUntilNextRun = $schedule['interval'] - $timeSinceLastRun;
            
            $nextRuns[$name] = [
                'interval' => $schedule['interval'],
                'last_run' => $schedule['last_run'],
                'next_run' => $currentTime + $timeUntilNextRun,
                'time_until' => max(0, $timeUntilNextRun)
            ];
        }
        
        return $nextRuns;
    }
    
    /**
     * 強制実行
     */
    public function forceRun($name) {
        if (!isset($this->schedules[$name])) {
            throw new Exception("Schedule not found: {$name}");
        }
        
        $schedule = $this->schedules[$name];
        $startTime = microtime(true);
        $result = call_user_func($schedule['callback']);
        $duration = microtime(true) - $startTime;
        
        $this->schedules[$name]['last_run'] = time();
        $this->log($name, $result, $duration);
        
        return [
            'name' => $name,
            'result' => $result,
            'duration' => $duration,
            'forced' => true
        ];
    }
    
    /**
     * スケジュールステータスを取得
     */
    public function getStatus() {
        $status = [];
        $currentTime = time();
        
        foreach ($this->schedules as $name => $schedule) {
            $status[$name] = [
                'interval_hours' => $schedule['interval'] / 3600,
                'last_run' => $schedule['last_run'] > 0 
                    ? date('Y-m-d H:i:s', $schedule['last_run'])
                    : 'Never',
                'hours_since_last' => round(($currentTime - $schedule['last_run']) / 3600, 2),
                'is_due' => ($currentTime - $schedule['last_run']) >= $schedule['interval']
            ];
        }
        
        return $status;
    }
}

// 使用例
echo "=== 定期クリーンアップスケジューラー ===\n";

$scheduler = new SessionCleanupScheduler('/tmp/cleanup_schedule_test.log');

// 標準スケジュールを設定
$scheduler->setupStandardSchedules();

// カスタムスケジュールを追加
$scheduler->addSchedule('custom_5min', 300, function() {
    $deleted = session_gc();
    return ['type' => 'custom', 'deleted' => $deleted];
});

// ステータス確認
echo "スケジュールステータス:\n";
$status = $scheduler->getStatus();
foreach ($status as $name => $info) {
    echo "  {$name}:\n";
    echo "    間隔: {$info['interval_hours']}時間\n";
    echo "    最終実行: {$info['last_run']}\n";
    echo "    実行必要: " . ($info['is_due'] ? 'Yes' : 'No') . "\n";
}

// スケジュール実行
echo "\n=== スケジュール実行 ===\n";
$executed = $scheduler->run();
foreach ($executed as $task) {
    echo "{$task['name']}: 実行完了\n";
    echo "  結果: " . json_encode($task['result']) . "\n";
    echo "  実行時間: " . round($task['duration'], 4) . "秒\n";
}

// 次回実行時刻
echo "\n=== 次回実行予定 ===\n";
$nextRuns = $scheduler->getNextRun();
foreach ($nextRuns as $name => $info) {
    echo "{$name}: " . round($info['time_until'] / 60, 2) . "分後\n";
}

// 強制実行
echo "\n=== 強制実行 ===\n";
$forced = $scheduler->forceRun('hourly_gc');
echo "{$forced['name']}: 強制実行完了\n";
echo "  削除: {$forced['result']['deleted']}件\n";

例5: セッションヘルスモニター

class SessionHealthMonitor {
    private $thresholds;
    private $alerts = [];
    
    /**
     * ヘルスモニターを初期化
     */
    public function __construct() {
        $this->thresholds = [
            'max_total_sessions' => 1000,
            'max_expired_sessions' => 100,
            'max_storage_mb' => 50,
            'max_average_size_kb' => 10,
            'max_oldest_age_hours' => 48
        ];
    }
    
    /**
     * しきい値を設定
     */
    public function setThreshold($key, $value) {
        $this->thresholds[$key] = $value;
    }
    
    /**
     * ヘルスチェックを実行
     */
    public function checkHealth() {
        $this->alerts = [];
        
        $sessionPath = session_save_path();
        if (empty($sessionPath)) {
            $sessionPath = sys_get_temp_dir();
        }
        
        $files = glob($sessionPath . '/sess_*');
        $totalSize = 0;
        $expiredCount = 0;
        $oldestTime = time();
        $gcMaxLifetime = ini_get('session.gc_maxlifetime');
        $cutoff = time() - $gcMaxLifetime;
        
        foreach ($files as $file) {
            $size = filesize($file);
            $mtime = filemtime($file);
            
            $totalSize += $size;
            
            if ($mtime < $cutoff) {
                $expiredCount++;
            }
            
            if ($mtime < $oldestTime) {
                $oldestTime = $mtime;
            }
        }
        
        $metrics = [
            'total_sessions' => count($files),
            'expired_sessions' => $expiredCount,
            'active_sessions' => count($files) - $expiredCount,
            'total_size_mb' => round($totalSize / 1024 / 1024, 2),
            'average_size_kb' => count($files) > 0 
                ? round($totalSize / count($files) / 1024, 2) 
                : 0,
            'oldest_age_hours' => round((time() - $oldestTime) / 3600, 2)
        ];
        
        // しきい値チェック
        $this->checkThresholds($metrics);
        
        $health = [
            'status' => empty($this->alerts) ? 'healthy' : 'unhealthy',
            'metrics' => $metrics,
            'alerts' => $this->alerts,
            'checked_at' => time()
        ];
        
        return $health;
    }
    
    /**
     * しきい値チェック
     */
    private function checkThresholds($metrics) {
        if ($metrics['total_sessions'] > $this->thresholds['max_total_sessions']) {
            $this->addAlert('warning', 'total_sessions', 
                "Total sessions ({$metrics['total_sessions']}) exceeds threshold ({$this->thresholds['max_total_sessions']})");
        }
        
        if ($metrics['expired_sessions'] > $this->thresholds['max_expired_sessions']) {
            $this->addAlert('warning', 'expired_sessions',
                "Expired sessions ({$metrics['expired_sessions']}) exceeds threshold ({$this->thresholds['max_expired_sessions']})");
        }
        
        if ($metrics['total_size_mb'] > $this->thresholds['max_storage_mb']) {
            $this->addAlert('warning', 'storage_size',
                "Total storage ({$metrics['total_size_mb']} MB) exceeds threshold ({$this->thresholds['max_storage_mb']} MB)");
        }
        
        if ($metrics['average_size_kb'] > $this->thresholds['max_average_size_kb']) {
            $this->addAlert('info', 'average_size',
                "Average session size ({$metrics['average_size_kb']} KB) exceeds threshold ({$this->thresholds['max_average_size_kb']} KB)");
        }
        
        if ($metrics['oldest_age_hours'] > $this->thresholds['max_oldest_age_hours']) {
            $this->addAlert('warning', 'oldest_age',
                "Oldest session age ({$metrics['oldest_age_hours']} hours) exceeds threshold ({$this->thresholds['max_oldest_age_hours']} hours)");
        }
    }
    
    /**
     * アラートを追加
     */
    private function addAlert($severity, $type, $message) {
        $this->alerts[] = [
            'severity' => $severity,
            'type' => $type,
            'message' => $message,
            'timestamp' => time()
        ];
    }
    
    /**
     * 自動修復を実行
     */
    public function autoRepair() {
        $health = $this->checkHealth();
        
        if ($health['status'] === 'healthy') {
            return [
                'repaired' => false,
                'reason' => 'System is healthy'
            ];
        }
        
        $actions = [];
        
        foreach ($health['alerts'] as $alert) {
            switch ($alert['type']) {
                case 'expired_sessions':
                case 'total_sessions':
                case 'oldest_age':
                    // GCを実行
                    $deleted = session_gc();
                    $actions[] = [
                        'action' => 'gc',
                        'trigger' => $alert['type'],
                        'deleted' => $deleted
                    ];
                    break;
                    
                case 'storage_size':
                    // アグレッシブクリーンアップ
                    $original = ini_get('session.gc_maxlifetime');
                    ini_set('session.gc_maxlifetime', 1800);  // 30分
                    $deleted = session_gc();
                    ini_set('session.gc_maxlifetime', $original);
                    
                    $actions[] = [
                        'action' => 'aggressive_cleanup',
                        'trigger' => $alert['type'],
                        'deleted' => $deleted
                    ];
                    break;
            }
        }
        
        // 修復後のヘルスチェック
        $afterHealth = $this->checkHealth();
        
        return [
            'repaired' => true,
            'before' => $health,
            'after' => $afterHealth,
            'actions' => $actions
        ];
    }
    
    /**
     * ヘルスレポートを生成
     */
    public function generateReport() {
        $health = $this->checkHealth();
        
        $report = "=== Session Health Report ===\n";
        $report .= "Status: " . strtoupper($health['status']) . "\n";
        $report .= "Checked: " . date('Y-m-d H:i:s', $health['checked_at']) . "\n\n";
        
        $report .= "Metrics:\n";
        foreach ($health['metrics'] as $key => $value) {
            $report .= "  {$key}: {$value}\n";
        }
        
        if (!empty($health['alerts'])) {
            $report .= "\nAlerts:\n";
            foreach ($health['alerts'] as $alert) {
                $report .= "  [{$alert['severity']}] {$alert['message']}\n";
            }
        } else {
            $report .= "\nNo alerts - system is healthy\n";
        }
        
        return $report;
    }
    
    /**
     * 継続的モニタリング
     */
    public function monitor($duration = 60, $interval = 10) {
        $endTime = time() + $duration;
        $samples = [];
        
        echo "Monitoring for {$duration} seconds...\n";
        
        while (time() < $endTime) {
            $health = $this->checkHealth();
            $samples[] = $health;
            
            echo "[" . date('H:i:s') . "] Status: {$health['status']}, ";
            echo "Sessions: {$health['metrics']['total_sessions']}, ";
            echo "Expired: {$health['metrics']['expired_sessions']}\n";
            
            if ($health['status'] === 'unhealthy') {
                echo "  ALERT: System unhealthy, auto-repairing...\n";
                $repair = $this->autoRepair();
                echo "  Repaired: " . ($repair['repaired'] ? 'Yes' : 'No') . "\n";
            }
            
            sleep($interval);
        }
        
        return $samples;
    }
}

// 使用例
echo "=== セッションヘルスモニター ===\n";

$monitor = new SessionHealthMonitor();

// しきい値を設定
$monitor->setThreshold('max_total_sessions', 50);
$monitor->setThreshold('max_expired_sessions', 10);

// ヘルスチェック
echo $monitor->generateReport();

// 自動修復
echo "\n=== 自動修復 ===\n";
$repair = $monitor->autoRepair();
if ($repair['repaired']) {
    echo "修復完了\n";
    echo "実行されたアクション: " . count($repair['actions']) . "件\n";
    foreach ($repair['actions'] as $action) {
        echo "  {$action['action']}: {$action['deleted']}件削除\n";
    }
} else {
    echo "修復不要: {$repair['reason']}\n";
}

// 継続的モニタリング(コメントアウト)
// echo "\n=== 継続的モニタリング ===\n";
// $samples = $monitor->monitor(30, 5);  // 30秒間、5秒間隔

例6: GC統計分析システム

class GarbageCollectionAnalytics {
    private $dataFile;
    
    /**
     * 分析システムを初期化
     */
    public function __construct($dataFile = '/tmp/gc_analytics.json') {
        $this->dataFile = $dataFile;
    }
    
    /**
     * GCを実行して記録
     */
    public function recordGC($metadata = []) {
        $beforeStats = $this->getSessionStats();
        
        $startTime = microtime(true);
        $deleted = session_gc();
        $duration = microtime(true) - $startTime;
        
        $afterStats = $this->getSessionStats();
        
        $record = [
            'timestamp' => time(),
            'deleted' => $deleted,
            'duration' => $duration,
            'before' => $beforeStats,
            'after' => $afterStats,
            'metadata' => $metadata
        ];
        
        $this->saveRecord($record);
        
        return $record;
    }
    
    /**
     * セッション統計を取得
     */
    private function getSessionStats() {
        $sessionPath = session_save_path();
        if (empty($sessionPath)) {
            $sessionPath = sys_get_temp_dir();
        }
        
        $files = glob($sessionPath . '/sess_*');
        $totalSize = 0;
        
        foreach ($files as $file) {
            $totalSize += filesize($file);
        }
        
        return [
            'count' => count($files),
            'size' => $totalSize
        ];
    }
    
    /**
     * レコードを保存
     */
    private function saveRecord($record) {
        $records = $this->loadRecords();
        $records[] = $record;
        
        // 最新1000件のみ保持
        if (count($records) > 1000) {
            $records = array_slice($records, -1000);
        }
        
        file_put_contents($this->dataFile, json_encode($records));
    }
    
    /**
     * レコードを読み込み
     */
    private function loadRecords() {
        if (!file_exists($this->dataFile)) {
            return [];
        }
        
        return json_decode(file_get_contents($this->dataFile), true) ?? [];
    }
    
    /**
     * 統計分析を実行
     */
    public function analyze($period = 86400) {  // デフォルト24時間
        $records = $this->loadRecords();
        $cutoff = time() - $period;
        
        // 期間内のレコードをフィルター
        $periodRecords = array_filter($records, function($r) use ($cutoff) {
            return $r['timestamp'] >= $cutoff;
        });
        
        if (empty($periodRecords)) {
            return ['error' => 'No records in specified period'];
        }
        
        $totalDeleted = array_sum(array_column($periodRecords, 'deleted'));
        $totalDuration = array_sum(array_column($periodRecords, 'duration'));
        $deletedCounts = array_column($periodRecords, 'deleted');
        
        $analysis = [
            'period_hours' => $period / 3600,
            'total_runs' => count($periodRecords),
            'total_deleted' => $totalDeleted,
            'average_deleted' => round($totalDeleted / count($periodRecords), 2),
            'median_deleted' => $this->median($deletedCounts),
            'max_deleted' => max($deletedCounts),
            'min_deleted' => min($deletedCounts),
            'total_duration' => round($totalDuration, 4),
            'average_duration' => round($totalDuration / count($periodRecords), 4),
            'efficiency' => $totalDuration > 0 
                ? round($totalDeleted / $totalDuration, 2)
                : 0  // sessions/second
        ];
        
        return $analysis;
    }
    
    /**
     * 中央値を計算
     */
    private function median($values) {
        sort($values);
        $count = count($values);
        $middle = floor($count / 2);
        
        if ($count % 2 == 0) {
            return ($values[$middle - 1] + $values[$middle]) / 2;
        }
        
        return $values[$middle];
    }
    
    /**
     * トレンド分析
     */
    public function analyzeTrend() {
        $records = $this->loadRecords();
        
        if (count($records) < 10) {
            return ['error' => 'Insufficient data for trend analysis'];
        }
        
        // 最近の50%と古い50%を比較
        $half = intval(count($records) / 2);
        $older = array_slice($records, 0, $half);
        $newer = array_slice($records, $half);
        
        $olderAvg = array_sum(array_column($older, 'deleted')) / count($older);
        $newerAvg = array_sum(array_column($newer, 'deleted')) / count($newer);
        
        $change = (($newerAvg - $olderAvg) / $olderAvg) * 100;
        
        $trend = 'stable';
        if ($change > 20) {
            $trend = 'increasing';
        } elseif ($change < -20) {
            $trend = 'decreasing';
        }
        
        return [
            'trend' => $trend,
            'change_percent' => round($change, 2),
            'older_average' => round($olderAvg, 2),
            'newer_average' => round($newerAvg, 2)
        ];
    }
    
    /**
     * レポートを生成
     */
    public function generateReport() {
        $analysis24h = $this->analyze(86400);
        $analysis7d = $this->analyze(604800);
        $trend = $this->analyzeTrend();
        
        $report = "=== GC Analytics Report ===\n\n";
        
        $report .= "Last 24 Hours:\n";
        if (!isset($analysis24h['error'])) {
            $report .= "  Total Runs: {$analysis24h['total_runs']}\n";
            $report .= "  Total Deleted: {$analysis24h['total_deleted']}\n";
            $report .= "  Average Deleted: {$analysis24h['average_deleted']}\n";
            $report .= "  Efficiency: {$analysis24h['efficiency']} sessions/second\n";
        } else {
            $report .= "  " . $analysis24h['error'] . "\n";
        }
        
        $report .= "\nLast 7 Days:\n";
        if (!isset($analysis7d['error'])) {
            $report .= "  Total Runs: {$analysis7d['total_runs']}\n";
            $report .= "  Total Deleted: {$analysis7d['total_deleted']}\n";
            $report .= "  Average Deleted: {$analysis7d['average_deleted']}\n";
        } else {
            $report .= "  " . $analysis7d['error'] . "\n";
        }
        
        $report .= "\nTrend Analysis:\n";
        if (!isset($trend['error'])) {
            $report .= "  Trend: {$trend['trend']}\n";
            $report .= "  Change: {$trend['change_percent']}%\n";
        } else {
            $report .= "  " . $trend['error'] . "\n";
        }
        
        return $report;
    }
}

// 使用例
echo "=== GC統計分析 ===\n";

$analytics = new GarbageCollectionAnalytics('/tmp/gc_analytics_test.json');

// GCを実行して記録
echo "GC実行と記録:\n";
for ($i = 0; $i < 5; $i++) {
    $record = $analytics->recordGC(['run' => $i + 1]);
    echo "  Run " . ($i + 1) . ": {$record['deleted']}件削除 ({$record['duration']}秒)\n";
    sleep(1);
}

// 分析実行
echo "\n=== 24時間の分析 ===\n";
$analysis = $analytics->analyze(86400);
if (!isset($analysis['error'])) {
    echo "総実行回数: {$analysis['total_runs']}\n";
    echo "総削除数: {$analysis['total_deleted']}\n";
    echo "平均削除数: {$analysis['average_deleted']}\n";
    echo "中央値: {$analysis['median_deleted']}\n";
    echo "効率: {$analysis['efficiency']} sessions/second\n";
}

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

GC設定のベストプラクティス

// 環境に応じたGC設定

// 開発環境(頻繁にクリーンアップ)
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 10);  // 10%
ini_set('session.gc_maxlifetime', 1800);  // 30分

// 本番環境(バランス重視)
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 100);  // 1%
ini_set('session.gc_maxlifetime', 86400);  // 24時間

// 高トラフィック環境(手動管理)
ini_set('session.gc_probability', 0);  // 自動GC無効
ini_set('session.gc_divisor', 1);
// cronで定期的にsession_gc()を実行

// 低トラフィック環境(確実にクリーンアップ)
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 1);  // 100%
ini_set('session.gc_maxlifetime', 3600);  // 1時間

まとめ

session_gc()関数の特徴をまとめると:

できること:

  • 期限切れセッションデータの削除
  • セッションストレージのクリーンアップ
  • ストレージ容量の最適化
  • 手動でのガベージコレクション実行

重要な注意点:

  • PHP 7.1.0以降で使用可能
  • session.gc_maxlifetimeに基づいて削除
  • セッション開始前でも実行可能
  • 削除されたセッション数を返す

自動GCの設定:

  • session.gc_probability / session.gc_divisor = 実行確率
  • デフォルト: 1/100 = 1%
  • session_start()時に確率的に実行

推奨される使用場面:

  • 定期的なセッションクリーンアップ
  • ストレージ容量管理
  • パフォーマンス最適化
  • セッション数の制御
  • cronジョブでの自動化

ベストプラクティス:

// 1. 定期的な手動実行(cronジョブ)
// 毎時間実行
$deleted = session_gc();
error_log("Session GC: {$deleted} sessions deleted");

// 2. しきい値ベースの実行
$sessionCount = count(glob(session_save_path() . '/sess_*'));
if ($sessionCount > 1000) {
    session_gc();
}

// 3. 条件付きアグレッシブクリーンアップ
$totalSize = /* 計算 */;
if ($totalSize > 50 * 1024 * 1024) {  // 50MB
    $original = ini_get('session.gc_maxlifetime');
    ini_set('session.gc_maxlifetime', 600);
    session_gc();
    ini_set('session.gc_maxlifetime', $original);
}

// 4. モニタリング付き実行
$before = count(glob(session_save_path() . '/sess_*'));
$deleted = session_gc();
$after = count(glob(session_save_path() . '/sess_*'));
// 統計を記録

関連設定:

  • session.gc_maxlifetime: 有効期限(秒)
  • session.gc_probability: 実行確率(分子)
  • session.gc_divisor: 実行確率(分母)
  • session.save_path: セッションファイルのパス

パフォーマンス考慮事項:

  • 大量のセッションがある場合、GC実行に時間がかかる
  • 本番環境では自動GCを無効にしてcronで実行を推奨
  • ストレージI/Oに負荷がかかることを考慮

session_gc()は、セッションストレージを健全に保つための重要な関数です。定期的なクリーンアップでパフォーマンスとストレージ容量を最適化しましょう!

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