[PHP]session_encode関数を完全解説!セッションデータをエンコードする方法

PHP

こんにちは!今回は、PHPの標準関数であるsession_encode()について詳しく解説していきます。セッションデータを文字列に変換できる、セッション管理の高度な関数です!

session_encode関数とは?

session_encode()関数は、現在の$_SESSION配列の内容をエンコードされた文字列に変換する関数です。

セッションデータのバックアップ、移行、カスタムストレージへの保存、デバッグなどに使用されます。session_decode()と対になる関数です!

基本的な構文

session_encode(): string|false
  • 引数: なし
  • 戻り値: エンコードされた文字列、失敗時はfalse

セッションハンドラーとエンコード形式

// PHPのセッションシリアライザー(ハンドラー)

// 1. php (デフォルト)
// 形式: key|serialized_value
ini_set('session.serialize_handler', 'php');
session_start();
$_SESSION['name'] = 'John';
$_SESSION['age'] = 30;
echo session_encode();
// 出力例: name|s:4:"John";age|i:30;

// 2. php_binary
// 形式: バイナリ形式(キー長をバイナリで表現)
ini_set('session.serialize_handler', 'php_binary');
session_start();
$_SESSION['name'] = 'John';
echo session_encode();
// バイナリデータ(読みにくい)

// 3. php_serialize
// 形式: 配列全体をserialize()
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['name'] = 'John';
$_SESSION['age'] = 30;
echo session_encode();
// 出力例: a:2:{s:4:"name";s:4:"John";s:3:"age";i:30;}

// 現在のハンドラーを確認
echo ini_get('session.serialize_handler');  // 通常は 'php'

重要な注意点

// session_start()後に使用する必要がある
session_start();
$encoded = session_encode();  // 正しい

// session_start()前では空文字列が返る
// $encoded = session_encode();  // 空または警告

// $_SESSIONの現在の状態をエンコード
session_start();
$_SESSION['user'] = 'alice';
$encoded1 = session_encode();

$_SESSION['role'] = 'admin';
$encoded2 = session_encode();
// $encoded1と$encoded2は異なる

// エンコードされた文字列は読み取り専用
// 手動で編集すると壊れる可能性がある

// ハンドラーが異なると形式も異なる
ini_set('session.serialize_handler', 'php');
$encoded_php = session_encode();

ini_set('session.serialize_handler', 'php_serialize');
$encoded_serialize = session_encode();
// 同じデータでも形式が異なる

session_decode()との関係

// エンコード: $_SESSION → 文字列
session_start();
$_SESSION['user'] = 'alice';
$_SESSION['role'] = 'admin';
$encoded = session_encode();
echo $encoded;  // user|s:5:"alice";role|s:5:"admin";

// デコード: 文字列 → $_SESSION
session_start();
$_SESSION = [];  // クリア
session_decode($encoded);
echo $_SESSION['user'];  // alice
echo $_SESSION['role'];  // admin

基本的な使用例

シンプルなエンコード

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

// データを設定
$_SESSION['username'] = 'alice';
$_SESSION['email'] = 'alice@example.com';
$_SESSION['login_time'] = time();

// エンコード
$encoded = session_encode();
echo "エンコード結果:\n";
echo $encoded . "\n";
echo "長さ: " . strlen($encoded) . " bytes\n";

異なるハンドラーでのエンコード

session_start();
$_SESSION['data'] = 'test';
$_SESSION['count'] = 42;

// phpハンドラー
ini_set('session.serialize_handler', 'php');
$encoded_php = session_encode();
echo "PHP形式: {$encoded_php}\n";

// php_serializeハンドラー
ini_set('session.serialize_handler', 'php_serialize');
$encoded_serialize = session_encode();
echo "PHP Serialize形式: {$encoded_serialize}\n";

セッションの保存と復元

// セッションを保存
session_start();
$_SESSION['user_id'] = 123;
$_SESSION['preferences'] = ['theme' => 'dark', 'lang' => 'ja'];

$backup = session_encode();
echo "バックアップ: {$backup}\n";

// セッションをクリア
$_SESSION = [];
echo "クリア後: " . (empty($_SESSION) ? '空' : '有り') . "\n";

// 復元
session_decode($backup);
echo "復元後のuser_id: {$_SESSION['user_id']}\n";

セッションデータのサイズ確認

session_start();

// データを追加
$_SESSION['small'] = 'test';
$_SESSION['medium'] = str_repeat('x', 1000);
$_SESSION['large'] = array_fill(0, 100, 'data');

$encoded = session_encode();
echo "セッションデータサイズ: " . strlen($encoded) . " bytes\n";

// キーごとのサイズを推定
foreach ($_SESSION as $key => $value) {
    $keySize = strlen(serialize($value));
    echo "{$key}: {$keySize} bytes\n";
}

実践的な使用例

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

class SessionBackupSystem {
    private $backupDir;
    private $handler;
    
    /**
     * バックアップシステムを初期化
     */
    public function __construct($backupDir = '/tmp/session_backups', $handler = 'php') {
        $this->backupDir = $backupDir;
        $this->handler = $handler;
        
        if (!is_dir($backupDir)) {
            mkdir($backupDir, 0755, true);
        }
    }
    
    /**
     * 現在のセッションをバックアップ
     */
    public function backup($label = null) {
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
        
        // ハンドラーを設定
        $originalHandler = ini_get('session.serialize_handler');
        ini_set('session.serialize_handler', $this->handler);
        
        // エンコード
        $encoded = session_encode();
        
        if ($encoded === false) {
            ini_set('session.serialize_handler', $originalHandler);
            throw new Exception("Failed to encode session");
        }
        
        // ハンドラーを元に戻す
        ini_set('session.serialize_handler', $originalHandler);
        
        // バックアップファイル名を生成
        $timestamp = date('YmdHis');
        $sessionId = session_id();
        $filename = $label ? "{$label}_{$timestamp}" : "backup_{$timestamp}_{$sessionId}";
        $filepath = $this->backupDir . '/' . $filename . '.dat';
        
        // メタデータと共に保存
        $backupData = [
            'timestamp' => time(),
            'session_id' => $sessionId,
            'handler' => $this->handler,
            'encoded_data' => $encoded,
            'keys' => array_keys($_SESSION),
            'label' => $label
        ];
        
        $result = file_put_contents($filepath, json_encode($backupData));
        
        if ($result === false) {
            throw new Exception("Failed to write backup file");
        }
        
        return [
            'filename' => $filename,
            'filepath' => $filepath,
            'size' => $result,
            'timestamp' => $backupData['timestamp'],
            'keys_count' => count($backupData['keys'])
        ];
    }
    
    /**
     * バックアップから復元
     */
    public function restore($filename) {
        $filepath = $this->backupDir . '/' . $filename . '.dat';
        
        if (!file_exists($filepath)) {
            throw new Exception("Backup file not found: {$filename}");
        }
        
        $backupData = json_decode(file_get_contents($filepath), true);
        
        if ($backupData === null) {
            throw new Exception("Invalid backup file");
        }
        
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
        
        // ハンドラーを設定
        $originalHandler = ini_get('session.serialize_handler');
        ini_set('session.serialize_handler', $backupData['handler']);
        
        // デコード
        $result = session_decode($backupData['encoded_data']);
        
        // ハンドラーを元に戻す
        ini_set('session.serialize_handler', $originalHandler);
        
        if (!$result) {
            throw new Exception("Failed to decode backup data");
        }
        
        return [
            'filename' => $filename,
            'timestamp' => $backupData['timestamp'],
            'restored_keys' => count($_SESSION),
            'original_keys' => count($backupData['keys'])
        ];
    }
    
    /**
     * バックアップ一覧を取得
     */
    public function listBackups() {
        $files = glob($this->backupDir . '/*.dat');
        $backups = [];
        
        foreach ($files as $file) {
            $data = json_decode(file_get_contents($file), true);
            
            if ($data !== null) {
                $backups[] = [
                    'filename' => basename($file, '.dat'),
                    'timestamp' => $data['timestamp'],
                    'date' => date('Y-m-d H:i:s', $data['timestamp']),
                    'size' => filesize($file),
                    'keys_count' => count($data['keys']),
                    'label' => $data['label'] ?? null,
                    'handler' => $data['handler']
                ];
            }
        }
        
        // タイムスタンプでソート(新しい順)
        usort($backups, function($a, $b) {
            return $b['timestamp'] - $a['timestamp'];
        });
        
        return $backups;
    }
    
    /**
     * バックアップを削除
     */
    public function deleteBackup($filename) {
        $filepath = $this->backupDir . '/' . $filename . '.dat';
        
        if (!file_exists($filepath)) {
            return false;
        }
        
        return unlink($filepath);
    }
    
    /**
     * 自動バックアップ(定期実行用)
     */
    public function autoBackup($maxBackups = 10) {
        // 新しいバックアップを作成
        $backup = $this->backup('auto');
        
        // 古いバックアップを削除
        $backups = $this->listBackups();
        $autoBackups = array_filter($backups, function($b) {
            return strpos($b['filename'], 'auto_') === 0;
        });
        
        if (count($autoBackups) > $maxBackups) {
            $toDelete = array_slice($autoBackups, $maxBackups);
            foreach ($toDelete as $old) {
                $this->deleteBackup($old['filename']);
            }
        }
        
        return [
            'backup' => $backup,
            'deleted' => count($autoBackups) - $maxBackups,
            'total_backups' => count($this->listBackups())
        ];
    }
    
    /**
     * バックアップの差分を表示
     */
    public function diff($filename1, $filename2) {
        $backup1 = json_decode(
            file_get_contents($this->backupDir . '/' . $filename1 . '.dat'),
            true
        );
        $backup2 = json_decode(
            file_get_contents($this->backupDir . '/' . $filename2 . '.dat'),
            true
        );
        
        if ($backup1 === null || $backup2 === null) {
            throw new Exception("Invalid backup files");
        }
        
        // データをデコード
        $originalHandler = ini_get('session.serialize_handler');
        
        ini_set('session.serialize_handler', $backup1['handler']);
        session_decode($backup1['encoded_data']);
        $data1 = $_SESSION;
        
        ini_set('session.serialize_handler', $backup2['handler']);
        session_decode($backup2['encoded_data']);
        $data2 = $_SESSION;
        
        ini_set('session.serialize_handler', $originalHandler);
        
        return [
            'only_in_backup1' => array_diff_key($data1, $data2),
            'only_in_backup2' => array_diff_key($data2, $data1),
            'common' => array_intersect_key($data1, $data2),
            'changed' => $this->findChanges($data1, $data2)
        ];
    }
    
    /**
     * 変更されたキーを検出
     */
    private function findChanges($data1, $data2) {
        $changes = [];
        
        foreach (array_intersect_key($data1, $data2) as $key => $value) {
            if ($data1[$key] !== $data2[$key]) {
                $changes[$key] = [
                    'old' => $data1[$key],
                    'new' => $data2[$key]
                ];
            }
        }
        
        return $changes;
    }
}

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

$backup = new SessionBackupSystem('/tmp/session_backup_test');

// セッション作成
session_start();
$_SESSION['user_id'] = 123;
$_SESSION['username'] = 'alice';
$_SESSION['email'] = 'alice@example.com';
echo "セッション作成: " . count($_SESSION) . "件のデータ\n";

// バックアップ作成
echo "\nバックアップ作成:\n";
$result1 = $backup->backup('initial');
echo "  ファイル名: {$result1['filename']}\n";
echo "  サイズ: {$result1['size']} bytes\n";
echo "  キー数: {$result1['keys_count']}\n";

// セッション変更
$_SESSION['role'] = 'admin';
$_SESSION['last_login'] = time();
unset($_SESSION['email']);
echo "\nセッション変更後: " . count($_SESSION) . "件\n";

// 2つ目のバックアップ
$result2 = $backup->backup('modified');
echo "2つ目のバックアップ: {$result2['filename']}\n";

// バックアップ一覧
echo "\n=== バックアップ一覧 ===\n";
foreach ($backup->listBackups() as $b) {
    echo "{$b['filename']}:\n";
    echo "  日時: {$b['date']}\n";
    echo "  サイズ: {$b['size']} bytes\n";
    echo "  キー数: {$b['keys_count']}\n";
}

// セッションをクリア
$_SESSION = [];
echo "\nセッションクリア\n";

// 復元
echo "\n復元:\n";
$restored = $backup->restore('initial');
echo "  復元されたキー数: {$restored['restored_keys']}\n";
echo "  username: {$_SESSION['username']}\n";

// 差分表示
echo "\n=== バックアップ差分 ===\n";
$diff = $backup->diff('initial', 'modified');
echo "backup1のみ: " . count($diff['only_in_backup1']) . "件\n";
echo "backup2のみ: " . count($diff['only_in_backup2']) . "件\n";
echo "変更: " . count($diff['changed']) . "件\n";

例2: セッションデータ分析ツール

class SessionDataAnalyzer {
    /**
     * セッションデータを分析
     */
    public function analyze() {
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
        
        $encoded = session_encode();
        
        if ($encoded === false) {
            throw new Exception("Failed to encode session");
        }
        
        $analysis = [
            'total_keys' => count($_SESSION),
            'encoded_size' => strlen($encoded),
            'handler' => ini_get('session.serialize_handler'),
            'keys_analysis' => [],
            'data_types' => [],
            'size_distribution' => [],
            'complexity_score' => 0
        ];
        
        // キーごとの分析
        foreach ($_SESSION as $key => $value) {
            $serialized = serialize($value);
            $type = gettype($value);
            
            $analysis['keys_analysis'][$key] = [
                'type' => $type,
                'size' => strlen($serialized),
                'depth' => $this->getDepth($value),
                'is_complex' => is_array($value) || is_object($value)
            ];
            
            // データ型の集計
            if (!isset($analysis['data_types'][$type])) {
                $analysis['data_types'][$type] = 0;
            }
            $analysis['data_types'][$type]++;
            
            // サイズ分布
            $sizeCategory = $this->getSizeCategory(strlen($serialized));
            if (!isset($analysis['size_distribution'][$sizeCategory])) {
                $analysis['size_distribution'][$sizeCategory] = 0;
            }
            $analysis['size_distribution'][$sizeCategory]++;
        }
        
        // 複雑度スコアを計算
        $analysis['complexity_score'] = $this->calculateComplexity($analysis);
        
        return $analysis;
    }
    
    /**
     * データの深さを取得
     */
    private function getDepth($value, $depth = 0) {
        if (!is_array($value)) {
            return $depth;
        }
        
        $maxDepth = $depth;
        foreach ($value as $item) {
            $itemDepth = $this->getDepth($item, $depth + 1);
            $maxDepth = max($maxDepth, $itemDepth);
        }
        
        return $maxDepth;
    }
    
    /**
     * サイズカテゴリを取得
     */
    private function getSizeCategory($size) {
        if ($size < 100) return 'small';
        if ($size < 1000) return 'medium';
        if ($size < 10000) return 'large';
        return 'xlarge';
    }
    
    /**
     * 複雑度スコアを計算
     */
    private function calculateComplexity($analysis) {
        $score = 0;
        
        // キー数による加算
        $score += $analysis['total_keys'] * 2;
        
        // 複雑なデータ型による加算
        foreach ($analysis['keys_analysis'] as $keyData) {
            if ($keyData['is_complex']) {
                $score += 10;
            }
            $score += $keyData['depth'] * 5;
        }
        
        return $score;
    }
    
    /**
     * レポートを生成
     */
    public function generateReport() {
        $analysis = $this->analyze();
        
        $report = [
            'summary' => [
                'total_keys' => $analysis['total_keys'],
                'total_size' => $analysis['encoded_size'],
                'average_key_size' => $analysis['total_keys'] > 0 
                    ? round($analysis['encoded_size'] / $analysis['total_keys'], 2) 
                    : 0,
                'handler' => $analysis['handler'],
                'complexity' => $this->getComplexityLevel($analysis['complexity_score'])
            ],
            'data_types' => $analysis['data_types'],
            'size_distribution' => $analysis['size_distribution'],
            'largest_keys' => $this->getLargestKeys($analysis['keys_analysis'], 5),
            'most_complex_keys' => $this->getMostComplexKeys($analysis['keys_analysis'], 5),
            'recommendations' => $this->generateRecommendations($analysis)
        ];
        
        return $report;
    }
    
    /**
     * 複雑度レベルを取得
     */
    private function getComplexityLevel($score) {
        if ($score < 50) return 'low';
        if ($score < 200) return 'medium';
        if ($score < 500) return 'high';
        return 'very_high';
    }
    
    /**
     * 最大サイズのキーを取得
     */
    private function getLargestKeys($keysAnalysis, $limit) {
        uasort($keysAnalysis, function($a, $b) {
            return $b['size'] - $a['size'];
        });
        
        return array_slice($keysAnalysis, 0, $limit, true);
    }
    
    /**
     * 最も複雑なキーを取得
     */
    private function getMostComplexKeys($keysAnalysis, $limit) {
        uasort($keysAnalysis, function($a, $b) {
            return $b['depth'] - $a['depth'];
        });
        
        return array_slice($keysAnalysis, 0, $limit, true);
    }
    
    /**
     * 推奨事項を生成
     */
    private function generateRecommendations($analysis) {
        $recommendations = [];
        
        if ($analysis['encoded_size'] > 10240) {
            $recommendations[] = "セッションサイズが大きすぎます({$analysis['encoded_size']} bytes)。データベースストレージの使用を検討してください。";
        }
        
        if ($analysis['total_keys'] > 50) {
            $recommendations[] = "セッションキーが多すぎます({$analysis['total_keys']}個)。必要なデータのみを保存してください。";
        }
        
        if ($analysis['complexity_score'] > 500) {
            $recommendations[] = "セッションデータが非常に複雑です。シンプルな構造への変更を検討してください。";
        }
        
        foreach ($analysis['keys_analysis'] as $key => $data) {
            if ($data['size'] > 5000) {
                $recommendations[] = "キー '{$key}' が大きすぎます({$data['size']} bytes)。";
            }
        }
        
        if (empty($recommendations)) {
            $recommendations[] = "セッションデータは適切に管理されています。";
        }
        
        return $recommendations;
    }
    
    /**
     * 視覚的なレポートを生成
     */
    public function visualReport() {
        $report = $this->generateReport();
        
        $output = "=== セッションデータ分析レポート ===\n\n";
        
        // サマリー
        $output .= "サマリー:\n";
        $output .= "  総キー数: {$report['summary']['total_keys']}\n";
        $output .= "  総サイズ: {$report['summary']['total_size']} bytes\n";
        $output .= "  平均キーサイズ: {$report['summary']['average_key_size']} bytes\n";
        $output .= "  ハンドラー: {$report['summary']['handler']}\n";
        $output .= "  複雑度: {$report['summary']['complexity']}\n\n";
        
        // データ型
        $output .= "データ型分布:\n";
        foreach ($report['data_types'] as $type => $count) {
            $output .= "  {$type}: {$count}\n";
        }
        $output .= "\n";
        
        // サイズ分布
        $output .= "サイズ分布:\n";
        foreach ($report['size_distribution'] as $category => $count) {
            $output .= "  {$category}: {$count}\n";
        }
        $output .= "\n";
        
        // 最大キー
        $output .= "最大サイズのキー:\n";
        foreach ($report['largest_keys'] as $key => $data) {
            $output .= "  {$key}: {$data['size']} bytes ({$data['type']})\n";
        }
        $output .= "\n";
        
        // 推奨事項
        $output .= "推奨事項:\n";
        foreach ($report['recommendations'] as $recommendation) {
            $output .= "  - {$recommendation}\n";
        }
        
        return $output;
    }
    
    /**
     * 時系列分析(複数回の分析結果を比較)
     */
    public function trackOverTime($label = null) {
        $analysis = $this->analyze();
        
        $record = [
            'timestamp' => time(),
            'label' => $label,
            'total_keys' => $analysis['total_keys'],
            'total_size' => $analysis['encoded_size'],
            'complexity_score' => $analysis['complexity_score']
        ];
        
        // ファイルに追記
        $trackingFile = sys_get_temp_dir() . '/session_tracking.json';
        $history = [];
        
        if (file_exists($trackingFile)) {
            $history = json_decode(file_get_contents($trackingFile), true) ?? [];
        }
        
        $history[] = $record;
        file_put_contents($trackingFile, json_encode($history));
        
        return $record;
    }
    
    /**
     * トレンド分析
     */
    public function analyzeTrend() {
        $trackingFile = sys_get_temp_dir() . '/session_tracking.json';
        
        if (!file_exists($trackingFile)) {
            return ['error' => 'No tracking data available'];
        }
        
        $history = json_decode(file_get_contents($trackingFile), true);
        
        if (count($history) < 2) {
            return ['error' => 'Insufficient data for trend analysis'];
        }
        
        $trend = [
            'total_records' => count($history),
            'size_trend' => $this->calculateTrend(array_column($history, 'total_size')),
            'keys_trend' => $this->calculateTrend(array_column($history, 'total_keys')),
            'complexity_trend' => $this->calculateTrend(array_column($history, 'complexity_score'))
        ];
        
        return $trend;
    }
    
    /**
     * トレンドを計算
     */
    private function calculateTrend($values) {
        $count = count($values);
        if ($count < 2) return 'stable';
        
        $first = array_slice($values, 0, intval($count / 2));
        $second = array_slice($values, intval($count / 2));
        
        $avgFirst = array_sum($first) / count($first);
        $avgSecond = array_sum($second) / count($second);
        
        $change = (($avgSecond - $avgFirst) / $avgFirst) * 100;
        
        if ($change > 10) return 'increasing';
        if ($change < -10) return 'decreasing';
        return 'stable';
    }
}

// 使用例
echo "=== セッションデータ分析 ===\n";

$analyzer = new SessionDataAnalyzer();

// セッションデータを作成
session_start();
$_SESSION = [
    'user_id' => 123,
    'username' => 'alice',
    'preferences' => [
        'theme' => 'dark',
        'language' => 'ja',
        'notifications' => ['email' => true, 'sms' => false]
    ],
    'cart' => array_fill(0, 10, ['id' => 1, 'name' => 'Product', 'price' => 1000]),
    'large_data' => str_repeat('x', 5000)
];

// 分析
echo "\n基本分析:\n";
$analysis = $analyzer->analyze();
echo "  総キー数: {$analysis['total_keys']}\n";
echo "  エンコードサイズ: {$analysis['encoded_size']} bytes\n";
echo "  複雑度スコア: {$analysis['complexity_score']}\n";

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

// 時系列追跡
$analyzer->trackOverTime('snapshot1');
$_SESSION['new_data'] = 'added';
$analyzer->trackOverTime('snapshot2');

例3: セッション同期システム

class SessionSyncSystem {
    private $syncFile;
    
    /**
     * 同期システムを初期化
     */
    public function __construct($syncFile = '/tmp/session_sync.dat') {
        $this->syncFile = $syncFile;
    }
    
    /**
     * 現在のセッションを同期ストレージに保存
     */
    public function push() {
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
        
        $encoded = session_encode();
        
        if ($encoded === false) {
            throw new Exception("Failed to encode session");
        }
        
        $syncData = [
            'timestamp' => microtime(true),
            'session_id' => session_id(),
            'encoded_data' => $encoded,
            'handler' => ini_get('session.serialize_handler'),
            'checksum' => md5($encoded)
        ];
        
        $result = file_put_contents($this->syncFile, json_encode($syncData));
        
        return [
            'success' => $result !== false,
            'size' => $result,
            'checksum' => $syncData['checksum'],
            'timestamp' => $syncData['timestamp']
        ];
    }
    
    /**
     * 同期ストレージから現在のセッションに取得
     */
    public function pull($merge = false) {
        if (!file_exists($this->syncFile)) {
            return [
                'success' => false,
                'error' => 'Sync file not found'
            ];
        }
        
        $syncData = json_decode(file_get_contents($this->syncFile), true);
        
        if ($syncData === null) {
            return [
                'success' => false,
                'error' => 'Invalid sync data'
            ];
        }
        
        // チェックサム検証
        if (md5($syncData['encoded_data']) !== $syncData['checksum']) {
            return [
                'success' => false,
                'error' => 'Checksum mismatch - data corrupted'
            ];
        }
        
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
        
        // 現在のデータを保存(マージ用)
        $currentData = $merge ? $_SESSION : [];
        
        // ハンドラーを設定
        $originalHandler = ini_get('session.serialize_handler');
        ini_set('session.serialize_handler', $syncData['handler']);
        
        // デコード
        $result = session_decode($syncData['encoded_data']);
        
        // ハンドラーを元に戻す
        ini_set('session.serialize_handler', $originalHandler);
        
        if (!$result) {
            return [
                'success' => false,
                'error' => 'Failed to decode sync data'
            ];
        }
        
        // マージ
        if ($merge) {
            $_SESSION = array_merge($currentData, $_SESSION);
        }
        
        return [
            'success' => true,
            'timestamp' => $syncData['timestamp'],
            'age' => microtime(true) - $syncData['timestamp'],
            'merged' => $merge
        ];
    }
    
    /**
     * 同期が必要かチェック
     */
    public function needsSync($maxAge = 60) {
        if (!file_exists($this->syncFile)) {
            return [
                'needs_sync' => true,
                'reason' => 'No sync file found'
            ];
        }
        
        $syncData = json_decode(file_get_contents($this->syncFile), true);
        
        if ($syncData === null) {
            return [
                'needs_sync' => true,
                'reason' => 'Invalid sync file'
            ];
        }
        
        $age = microtime(true) - $syncData['timestamp'];
        
        return [
            'needs_sync' => $age > $maxAge,
            'reason' => $age > $maxAge ? 'Sync data is stale' : 'Sync data is fresh',
            'age' => $age,
            'max_age' => $maxAge
        ];
    }
    
    /**
     * 双方向同期(競合解決付き)
     */
    public function bidirectionalSync($conflictResolver = 'newest_wins') {
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
        
        // 現在のセッションをエンコード
        $currentEncoded = session_encode();
        $currentChecksum = md5($currentEncoded);
        
        // 同期ファイルを読み込み
        if (!file_exists($this->syncFile)) {
            // 同期ファイルがない場合は push のみ
            return $this->push();
        }
        
        $syncData = json_decode(file_get_contents($this->syncFile), true);
        
        // チェックサムが同じなら同期不要
        if ($syncData['checksum'] === $currentChecksum) {
            return [
                'action' => 'none',
                'message' => 'Already in sync'
            ];
        }
        
        // 競合解決
        switch ($conflictResolver) {
            case 'newest_wins':
                // タイムスタンプで判定
                if ($syncData['timestamp'] > time() - 1) {
                    // 同期データの方が新しい
                    $this->pull();
                    return [
                        'action' => 'pull',
                        'message' => 'Pulled newer data from sync'
                    ];
                } else {
                    // 現在のデータの方が新しい
                    $this->push();
                    return [
                        'action' => 'push',
                        'message' => 'Pushed newer data to sync'
                    ];
                }
                
            case 'merge':
                // 両方をマージ
                $this->pull(true);  // マージして pull
                $this->push();       // 結果を push
                return [
                    'action' => 'merge',
                    'message' => 'Merged and synced'
                ];
                
            default:
                return [
                    'action' => 'error',
                    'message' => 'Unknown conflict resolver'
                ];
        }
    }
    
    /**
     * 同期履歴を記録
     */
    public function logSync($action) {
        $logFile = sys_get_temp_dir() . '/session_sync_log.json';
        $logs = [];
        
        if (file_exists($logFile)) {
            $logs = json_decode(file_get_contents($logFile), true) ?? [];
        }
        
        $logs[] = [
            'timestamp' => microtime(true),
            'action' => $action,
            'session_id' => session_id()
        ];
        
        // 最新100件のみ保持
        if (count($logs) > 100) {
            $logs = array_slice($logs, -100);
        }
        
        file_put_contents($logFile, json_encode($logs));
    }
    
    /**
     * 同期統計を取得
     */
    public function getSyncStats() {
        $logFile = sys_get_temp_dir() . '/session_sync_log.json';
        
        if (!file_exists($logFile)) {
            return ['error' => 'No sync logs available'];
        }
        
        $logs = json_decode(file_get_contents($logFile), true);
        
        $stats = [
            'total_syncs' => count($logs),
            'actions' => [],
            'last_sync' => null
        ];
        
        foreach ($logs as $log) {
            $action = $log['action'];
            if (!isset($stats['actions'][$action])) {
                $stats['actions'][$action] = 0;
            }
            $stats['actions'][$action]++;
            
            if ($stats['last_sync'] === null || $log['timestamp'] > $stats['last_sync']) {
                $stats['last_sync'] = $log['timestamp'];
            }
        }
        
        return $stats;
    }
}

// 使用例
echo "=== セッション同期システム ===\n";

$sync = new SessionSyncSystem('/tmp/session_sync_test.dat');

// セッション作成
session_start();
$_SESSION['user_id'] = 123;
$_SESSION['data'] = 'original';
echo "セッション作成\n";

// Push
echo "\nPush:\n";
$pushResult = $sync->push();
echo "  成功: " . ($pushResult['success'] ? 'Yes' : 'No') . "\n";
echo "  チェックサム: {$pushResult['checksum']}\n";

// セッション変更
$_SESSION['data'] = 'modified';
$_SESSION['new_key'] = 'new_value';
echo "\nセッション変更\n";

// 同期が必要かチェック
$needsSync = $sync->needsSync(5);
echo "\n同期必要: " . ($needsSync['needs_sync'] ? 'Yes' : 'No') . "\n";
echo "  理由: {$needsSync['reason']}\n";

// Pull(元に戻る)
echo "\nPull:\n";
$pullResult = $sync->pull();
echo "  成功: " . ($pullResult['success'] ? 'Yes' : 'No') . "\n";
echo "  data: {$_SESSION['data']}\n";  // 'original'に戻る
echo "  new_key存在: " . (isset($_SESSION['new_key']) ? 'Yes' : 'No') . "\n";

// 双方向同期
$_SESSION['local_change'] = 'test';
echo "\n双方向同期:\n";
$biResult = $sync->bidirectionalSync('newest_wins');
echo "  アクション: {$biResult['action']}\n";
echo "  メッセージ: {$biResult['message']}\n";

例4: セッションエクスポート・インポートシステム

class SessionPortabilitySystem {
    /**
     * セッションを様々な形式でエクスポート
     */
    public function export($format = 'json', $includeMetadata = true) {
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
        
        $encoded = session_encode();
        
        if ($encoded === false) {
            throw new Exception("Failed to encode session");
        }
        
        $metadata = [
            'timestamp' => time(),
            'session_id' => session_id(),
            'handler' => ini_get('session.serialize_handler'),
            'php_version' => PHP_VERSION
        ];
        
        switch ($format) {
            case 'json':
                return $this->exportJson($_SESSION, $metadata, $includeMetadata);
                
            case 'xml':
                return $this->exportXml($_SESSION, $metadata, $includeMetadata);
                
            case 'csv':
                return $this->exportCsv($_SESSION, $metadata, $includeMetadata);
                
            case 'native':
                return $this->exportNative($encoded, $metadata, $includeMetadata);
                
            default:
                throw new Exception("Unsupported format: {$format}");
        }
    }
    
    /**
     * JSON形式でエクスポート
     */
    private function exportJson($data, $metadata, $includeMetadata) {
        $export = $includeMetadata 
            ? ['metadata' => $metadata, 'data' => $data]
            : $data;
        
        return json_encode($export, JSON_PRETTY_PRINT);
    }
    
    /**
     * XML形式でエクスポート
     */
    private function exportXml($data, $metadata, $includeMetadata) {
        $xml = new SimpleXMLElement('<session/>');
        
        if ($includeMetadata) {
            $metaNode = $xml->addChild('metadata');
            foreach ($metadata as $key => $value) {
                $metaNode->addChild($key, htmlspecialchars($value));
            }
        }
        
        $dataNode = $xml->addChild('data');
        $this->arrayToXml($data, $dataNode);
        
        return $xml->asXML();
    }
    
    /**
     * 配列をXMLに変換
     */
    private function arrayToXml($data, &$xml) {
        foreach ($data as $key => $value) {
            if (is_array($value)) {
                $subnode = $xml->addChild("item");
                $subnode->addAttribute('key', $key);
                $this->arrayToXml($value, $subnode);
            } else {
                $xml->addChild("item", htmlspecialchars($value))
                    ->addAttribute('key', $key);
            }
        }
    }
    
    /**
     * CSV形式でエクスポート
     */
    private function exportCsv($data, $metadata, $includeMetadata) {
        $output = fopen('php://temp', 'r+');
        
        if ($includeMetadata) {
            fputcsv($output, ['# Metadata']);
            foreach ($metadata as $key => $value) {
                fputcsv($output, [$key, $value]);
            }
            fputcsv($output, []);
        }
        
        fputcsv($output, ['Key', 'Value', 'Type']);
        
        foreach ($data as $key => $value) {
            fputcsv($output, [
                $key,
                is_scalar($value) ? $value : json_encode($value),
                gettype($value)
            ]);
        }
        
        rewind($output);
        $csv = stream_get_contents($output);
        fclose($output);
        
        return $csv;
    }
    
    /**
     * ネイティブ形式でエクスポート
     */
    private function exportNative($encoded, $metadata, $includeMetadata) {
        return $includeMetadata 
            ? json_encode(['metadata' => $metadata, 'encoded' => $encoded])
            : $encoded;
    }
    
    /**
     * 様々な形式からインポート
     */
    public function import($data, $format = 'json') {
        switch ($format) {
            case 'json':
                return $this->importJson($data);
                
            case 'native':
                return $this->importNative($data);
                
            default:
                throw new Exception("Unsupported import format: {$format}");
        }
    }
    
    /**
     * JSON形式からインポート
     */
    private function importJson($json) {
        $data = json_decode($json, true);
        
        if ($data === null) {
            throw new Exception("Invalid JSON data");
        }
        
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
        
        // メタデータがある場合は分離
        if (isset($data['metadata']) && isset($data['data'])) {
            $_SESSION = $data['data'];
            return [
                'success' => true,
                'metadata' => $data['metadata'],
                'keys_imported' => count($data['data'])
            ];
        }
        
        $_SESSION = $data;
        return [
            'success' => true,
            'keys_imported' => count($data)
        ];
    }
    
    /**
     * ネイティブ形式からインポート
     */
    private function importNative($data) {
        $decoded = json_decode($data, true);
        
        if ($decoded !== null && isset($decoded['encoded'])) {
            // メタデータ付きネイティブ形式
            $encoded = $decoded['encoded'];
            $metadata = $decoded['metadata'];
        } else {
            // エンコードされた文字列のみ
            $encoded = $data;
            $metadata = null;
        }
        
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
        
        $result = session_decode($encoded);
        
        return [
            'success' => $result,
            'metadata' => $metadata,
            'keys_imported' => $result ? count($_SESSION) : 0
        ];
    }
    
    /**
     * ファイルにエクスポート
     */
    public function exportToFile($filepath, $format = 'json') {
        $data = $this->export($format);
        $result = file_put_contents($filepath, $data);
        
        return [
            'success' => $result !== false,
            'filepath' => $filepath,
            'size' => $result,
            'format' => $format
        ];
    }
    
    /**
     * ファイルからインポート
     */
    public function importFromFile($filepath, $format = 'json') {
        if (!file_exists($filepath)) {
            throw new Exception("File not found: {$filepath}");
        }
        
        $data = file_get_contents($filepath);
        return $this->import($data, $format);
    }
    
    /**
     * 複数形式で一括エクスポート
     */
    public function exportAll($directory) {
        if (!is_dir($directory)) {
            mkdir($directory, 0755, true);
        }
        
        $formats = ['json', 'xml', 'csv', 'native'];
        $results = [];
        
        foreach ($formats as $format) {
            $filename = "session_export_{$format}." . ($format === 'native' ? 'dat' : $format);
            $filepath = $directory . '/' . $filename;
            
            $results[$format] = $this->exportToFile($filepath, $format);
        }
        
        return $results;
    }
}

// 使用例
echo "=== セッションエクスポート・インポート ===\n";

$porter = new SessionPortabilitySystem();

// セッション作成
session_start();
$_SESSION = [
    'user_id' => 123,
    'username' => 'alice',
    'preferences' => ['theme' => 'dark', 'lang' => 'ja'],
    'cart_items' => 5
];
echo "セッション作成: " . count($_SESSION) . "件\n";

// JSON エクスポート
echo "\nJSON エクスポート:\n";
$jsonExport = $porter->export('json');
echo substr($jsonExport, 0, 200) . "...\n";

// CSV エクスポート
echo "\nCSV エクスポート:\n";
$csvExport = $porter->export('csv');
echo substr($csvExport, 0, 200) . "...\n";

// ファイルにエクスポート
echo "\nファイルにエクスポート:\n";
$fileResult = $porter->exportToFile('/tmp/session_export.json', 'json');
echo "  成功: " . ($fileResult['success'] ? 'Yes' : 'No') . "\n";
echo "  サイズ: {$fileResult['size']} bytes\n";

// セッションをクリア
$_SESSION = [];
echo "\nセッションクリア\n";

// ファイルからインポート
echo "\nファイルからインポート:\n";
$importResult = $porter->importFromFile('/tmp/session_export.json', 'json');
echo "  成功: " . ($importResult['success'] ? 'Yes' : 'No') . "\n";
echo "  インポートキー数: {$importResult['keys_imported']}\n";
echo "  復元されたusername: {$_SESSION['username']}\n";

// 一括エクスポート
echo "\n一括エクスポート:\n";
$allResults = $porter->exportAll('/tmp/session_exports');
foreach ($allResults as $format => $result) {
    echo "  {$format}: " . ($result['success'] ? '成功' : '失敗') . " ({$result['size']} bytes)\n";
}

例5: セッションバージョン管理システム

class SessionVersionControl {
    private $versionDir;
    private $maxVersions;
    
    /**
     * バージョン管理を初期化
     */
    public function __construct($versionDir = '/tmp/session_versions', $maxVersions = 10) {
        $this->versionDir = $versionDir;
        $this->maxVersions = $maxVersions;
        
        if (!is_dir($versionDir)) {
            mkdir($versionDir, 0755, true);
        }
    }
    
    /**
     * 現在のセッション状態をコミット
     */
    public function commit($message = '') {
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
        
        $encoded = session_encode();
        
        if ($encoded === false) {
            throw new Exception("Failed to encode session");
        }
        
        // バージョン番号を取得
        $versionNumber = $this->getNextVersionNumber();
        
        $version = [
            'version' => $versionNumber,
            'timestamp' => microtime(true),
            'message' => $message,
            'session_id' => session_id(),
            'handler' => ini_get('session.serialize_handler'),
            'encoded_data' => $encoded,
            'data_snapshot' => $_SESSION,
            'checksum' => md5($encoded)
        ];
        
        $filename = sprintf('v%04d.json', $versionNumber);
        $filepath = $this->versionDir . '/' . $filename;
        
        file_put_contents($filepath, json_encode($version));
        
        // 古いバージョンを削除
        $this->pruneOldVersions();
        
        return [
            'version' => $versionNumber,
            'filename' => $filename,
            'message' => $message,
            'timestamp' => $version['timestamp']
        ];
    }
    
    /**
     * 次のバージョン番号を取得
     */
    private function getNextVersionNumber() {
        $versions = $this->listVersions();
        
        if (empty($versions)) {
            return 1;
        }
        
        return max(array_column($versions, 'version')) + 1;
    }
    
    /**
     * バージョン一覧を取得
     */
    public function listVersions() {
        $files = glob($this->versionDir . '/v*.json');
        $versions = [];
        
        foreach ($files as $file) {
            $data = json_decode(file_get_contents($file), true);
            
            if ($data !== null) {
                $versions[] = [
                    'version' => $data['version'],
                    'timestamp' => $data['timestamp'],
                    'date' => date('Y-m-d H:i:s', intval($data['timestamp'])),
                    'message' => $data['message'],
                    'keys_count' => count($data['data_snapshot']),
                    'checksum' => $data['checksum']
                ];
            }
        }
        
        // バージョンでソート
        usort($versions, function($a, $b) {
            return $b['version'] - $a['version'];
        });
        
        return $versions;
    }
    
    /**
     * 特定のバージョンにチェックアウト
     */
    public function checkout($version) {
        $filename = sprintf('v%04d.json', $version);
        $filepath = $this->versionDir . '/' . $filename;
        
        if (!file_exists($filepath)) {
            throw new Exception("Version not found: {$version}");
        }
        
        $versionData = json_decode(file_get_contents($filepath), true);
        
        if ($versionData === null) {
            throw new Exception("Invalid version file");
        }
        
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
        
        // ハンドラーを設定
        $originalHandler = ini_get('session.serialize_handler');
        ini_set('session.serialize_handler', $versionData['handler']);
        
        // デコード
        $result = session_decode($versionData['encoded_data']);
        
        // ハンドラーを元に戻す
        ini_set('session.serialize_handler', $originalHandler);
        
        if (!$result) {
            throw new Exception("Failed to decode version data");
        }
        
        return [
            'version' => $version,
            'message' => $versionData['message'],
            'timestamp' => $versionData['timestamp'],
            'restored_keys' => count($_SESSION)
        ];
    }
    
    /**
     * バージョン間の差分を表示
     */
    public function diff($version1, $version2) {
        $v1 = $this->getVersionData($version1);
        $v2 = $this->getVersionData($version2);
        
        $diff = [
            'added' => array_diff_key($v2['data_snapshot'], $v1['data_snapshot']),
            'removed' => array_diff_key($v1['data_snapshot'], $v2['data_snapshot']),
            'modified' => []
        ];
        
        foreach (array_intersect_key($v1['data_snapshot'], $v2['data_snapshot']) as $key => $value) {
            if ($v1['data_snapshot'][$key] !== $v2['data_snapshot'][$key]) {
                $diff['modified'][$key] = [
                    'old' => $v1['data_snapshot'][$key],
                    'new' => $v2['data_snapshot'][$key]
                ];
            }
        }
        
        return $diff;
    }
    
    /**
     * バージョンデータを取得
     */
    private function getVersionData($version) {
        $filename = sprintf('v%04d.json', $version);
        $filepath = $this->versionDir . '/' . $filename;
        
        if (!file_exists($filepath)) {
            throw new Exception("Version not found: {$version}");
        }
        
        return json_decode(file_get_contents($filepath), true);
    }
    
    /**
     * ログを表示
     */
    public function log($limit = 10) {
        $versions = $this->listVersions();
        
        return array_slice($versions, 0, $limit);
    }
    
    /**
     * 古いバージョンを削除
     */
    private function pruneOldVersions() {
        $versions = $this->listVersions();
        
        if (count($versions) <= $this->maxVersions) {
            return 0;
        }
        
        $toDelete = array_slice($versions, $this->maxVersions);
        $deleted = 0;
        
        foreach ($toDelete as $version) {
            $filename = sprintf('v%04d.json', $version['version']);
            $filepath = $this->versionDir . '/' . $filename;
            
            if (unlink($filepath)) {
                $deleted++;
            }
        }
        
        return $deleted;
    }
    
    /**
     * タグを作成
     */
    public function tag($tagName, $version = null) {
        if ($version === null) {
            // 最新バージョンにタグ付け
            $versions = $this->listVersions();
            $version = $versions[0]['version'];
        }
        
        $tagFile = $this->versionDir . '/tag_' . $tagName . '.json';
        
        file_put_contents($tagFile, json_encode([
            'tag' => $tagName,
            'version' => $version,
            'created_at' => time()
        ]));
        
        return [
            'tag' => $tagName,
            'version' => $version
        ];
    }
    
    /**
     * タグからチェックアウト
     */
    public function checkoutTag($tagName) {
        $tagFile = $this->versionDir . '/tag_' . $tagName . '.json';
        
        if (!file_exists($tagFile)) {
            throw new Exception("Tag not found: {$tagName}");
        }
        
        $tagData = json_decode(file_get_contents($tagFile), true);
        
        return $this->checkout($tagData['version']);
    }
}

// 使用例
echo "=== セッションバージョン管理 ===\n";

$vcs = new SessionVersionControl('/tmp/session_vcs_test', 5);

// セッション初期化
session_start();
$_SESSION = ['user_id' => 123, 'username' => 'alice'];

// v1 コミット
echo "v1 コミット:\n";
$commit1 = $vcs->commit('Initial session');
echo "  バージョン: {$commit1['version']}\n";
echo "  メッセージ: {$commit1['message']}\n";

// データ変更
$_SESSION['email'] = 'alice@example.com';

// v2 コミット
$commit2 = $vcs->commit('Added email');
echo "\nv2 コミット:\n";
echo "  バージョン: {$commit2['version']}\n";

// さらに変更
$_SESSION['role'] = 'admin';
unset($_SESSION['username']);

// v3 コミット
$commit3 = $vcs->commit('Changed role, removed username');
echo "\nv3 コミット:\n";
echo "  バージョン: {$commit3['version']}\n";

// ログ表示
echo "\n=== コミットログ ===\n";
foreach ($vcs->log(5) as $version) {
    echo "v{$version['version']}: {$version['message']} ({$version['date']})\n";
}

// 差分表示
echo "\n=== v1 と v3 の差分 ===\n";
$diff = $vcs->diff(1, 3);
echo "追加: " . count($diff['added']) . "件\n";
echo "削除: " . count($diff['removed']) . "件\n";
echo "変更: " . count($diff['modified']) . "件\n";

// v1 にチェックアウト
echo "\nv1 にチェックアウト:\n";
$checkout = $vcs->checkout(1);
echo "  復元されたキー: {$checkout['restored_keys']}\n";
echo "  username: {$_SESSION['username']}\n";
echo "  email存在: " . (isset($_SESSION['email']) ? 'Yes' : 'No') . "\n";

// タグ作成
echo "\nタグ作成:\n";
$tag = $vcs->tag('stable', 2);
echo "  タグ '{$tag['tag']}' をバージョン {$tag['version']} に作成\n";

例6: セッションデバッグコンソール

class SessionDebugConsole {
    private $history = [];
    
    /**
     * 現在のセッション状態を表示
     */
    public function showCurrent() {
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
        
        $encoded = session_encode();
        
        echo "=== Current Session State ===\n";
        echo "Session ID: " . session_id() . "\n";
        echo "Handler: " . ini_get('session.serialize_handler') . "\n";
        echo "Total Keys: " . count($_SESSION) . "\n";
        echo "Encoded Size: " . strlen($encoded) . " bytes\n";
        echo "\nEncoded Data:\n";
        echo $encoded . "\n";
        echo "\nDecoded Data:\n";
        print_r($_SESSION);
    }
    
    /**
     * セッションの変更を追跡
     */
    public function trackChanges() {
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
        
        $encoded = session_encode();
        $snapshot = [
            'timestamp' => microtime(true),
            'encoded' => $encoded,
            'data' => $_SESSION,
            'checksum' => md5($encoded)
        ];
        
        $this->history[] = $snapshot;
        
        if (count($this->history) > 1) {
            $previous = $this->history[count($this->history) - 2];
            
            echo "=== Changes Detected ===\n";
            
            if ($previous['checksum'] === $snapshot['checksum']) {
                echo "No changes\n";
            } else {
                $this->showDiff($previous['data'], $snapshot['data']);
            }
        }
    }
    
    /**
     * 差分を表示
     */
    private function showDiff($old, $new) {
        $added = array_diff_key($new, $old);
        $removed = array_diff_key($old, $new);
        $modified = [];
        
        foreach (array_intersect_key($old, $new) as $key => $value) {
            if ($old[$key] !== $new[$key]) {
                $modified[$key] = [
                    'old' => $old[$key],
                    'new' => $new[$key]
                ];
            }
        }
        
        if (!empty($added)) {
            echo "\nAdded:\n";
            foreach ($added as $key => $value) {
                echo "  + {$key}: " . var_export($value, true) . "\n";
            }
        }
        
        if (!empty($removed)) {
            echo "\nRemoved:\n";
            foreach ($removed as $key => $value) {
                echo "  - {$key}: " . var_export($value, true) . "\n";
            }
        }
        
        if (!empty($modified)) {
            echo "\nModified:\n";
            foreach ($modified as $key => $change) {
                echo "  ~ {$key}:\n";
                echo "    old: " . var_export($change['old'], true) . "\n";
                echo "    new: " . var_export($change['new'], true) . "\n";
            }
        }
    }
    
    /**
     * エンコード形式を比較
     */
    public function compareHandlers() {
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
        
        $handlers = ['php', 'php_serialize'];
        $originalHandler = ini_get('session.serialize_handler');
        
        echo "=== Handler Comparison ===\n";
        
        foreach ($handlers as $handler) {
            ini_set('session.serialize_handler', $handler);
            $encoded = session_encode();
            
            echo "\nHandler: {$handler}\n";
            echo "Size: " . strlen($encoded) . " bytes\n";
            echo "Data: {$encoded}\n";
        }
        
        ini_set('session.serialize_handler', $originalHandler);
    }
    
    /**
     * インタラクティブコンソール
     */
    public function interactive() {
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
        
        echo "=== Session Debug Console ===\n";
        echo "Commands: show, track, compare, encode, decode, set, get, unset, quit\n\n";
        
        while (true) {
            echo "> ";
            $input = trim(fgets(STDIN));
            $parts = explode(' ', $input, 2);
            $command = $parts[0];
            $args = $parts[1] ?? '';
            
            switch ($command) {
                case 'show':
                    $this->showCurrent();
                    break;
                    
                case 'track':
                    $this->trackChanges();
                    break;
                    
                case 'compare':
                    $this->compareHandlers();
                    break;
                    
                case 'encode':
                    echo session_encode() . "\n";
                    break;
                    
                case 'set':
                    list($key, $value) = explode('=', $args, 2);
                    $_SESSION[trim($key)] = trim($value);
                    echo "Set: {$key} = {$value}\n";
                    break;
                    
                case 'get':
                    $key = trim($args);
                    echo $_SESSION[$key] ?? 'undefined' . "\n";
                    break;
                    
                case 'unset':
                    $key = trim($args);
                    unset($_SESSION[$key]);
                    echo "Unset: {$key}\n";
                    break;
                    
                case 'quit':
                    return;
                    
                default:
                    echo "Unknown command: {$command}\n";
            }
        }
    }
}

// 使用例(非インタラクティブ)
echo "=== セッションデバッグコンソール ===\n";

$console = new SessionDebugConsole();

// セッション作成
session_start();
$_SESSION['user_id'] = 123;
$_SESSION['username'] = 'alice';

// 現在の状態を表示
$console->showCurrent();

// 変更を追跡
$console->trackChanges();

// データ変更
$_SESSION['role'] = 'admin';

// 変更を追跡
echo "\n";
$console->trackChanges();

// ハンドラー比較
echo "\n";
$console->compareHandlers();

// インタラクティブモード(コメントアウト)
// $console->interactive();

セッションハンドラーの選択

// 使用場面に応じたハンドラーの選択

// 1. php (デフォルト) - 一般的な用途
ini_set('session.serialize_handler', 'php');
// メリット: 互換性が高い、読みやすい
// デメリット: やや冗長

// 2. php_serialize - 複雑なデータ構造
ini_set('session.serialize_handler', 'php_serialize');
// メリット: 配列全体を扱える、安全
// デメリット: サイズが大きくなる可能性

// 3. php_binary - パフォーマンス重視
ini_set('session.serialize_handler', 'php_binary');
// メリット: コンパクト、高速
// デメリット: バイナリのため読みにくい

まとめ

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

できること:

  • $_SESSION配列を文字列に変換
  • セッションデータのシリアライゼーション
  • バックアップ・復元の実現
  • データの移行・同期

session_decode()との関係:

  • session_encode(): $_SESSION → 文字列
  • session_decode(): 文字列 → $_SESSION
  • 対になる関数

エンコード形式:

  • php: key|serialized_value (デフォルト)
  • php_serialize: 配列全体をserialize()
  • php_binary: バイナリ形式

推奨される使用場面:

  • セッションバックアップシステム
  • セッションデータの移行
  • デバッグ・監視ツール
  • カスタムセッションストレージ
  • バージョン管理システム
  • データ分析

重要な注意点:

  • session_start()後に使用
  • ハンドラーによって形式が異なる
  • エンコードされた文字列を手動で編集しない
  • デコード時は同じハンドラーを使用

ベストプラクティス:

// 1. 安全なバックアップ
session_start();
$handler = ini_get('session.serialize_handler');
$encoded = session_encode();
$backup = [
    'handler' => $handler,
    'data' => $encoded,
    'timestamp' => time()
];

// 2. ハンドラーを保持
$originalHandler = ini_get('session.serialize_handler');
ini_set('session.serialize_handler', 'php_serialize');
$encoded = session_encode();
ini_set('session.serialize_handler', $originalHandler);

// 3. チェックサム付き保存
$encoded = session_encode();
$checksum = md5($encoded);
file_put_contents('backup.dat', json_encode([
    'encoded' => $encoded,
    'checksum' => $checksum
]));

関連関数:

  • session_decode(): 文字列から復元
  • session_start(): セッション開始
  • serialize(): データをシリアライズ
  • unserialize(): データをアンシリアライズ

session_encode()は、セッションデータを文字列に変換できる強力な関数です。バックアップ、移行、デバッグなど、高度なセッション管理に活用しましょう!

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