[PHP]session_commit関数を完全解説!セッションを保存して終了する方法

PHP

こんにちは!今回は、PHPの標準関数であるsession_commit()について詳しく解説していきます。セッションデータを保存してロックを解放できる、パフォーマンス最適化に重要な関数です!

session_commit関数とは?

session_commit()関数は、セッションデータを書き込んでセッションを終了する関数です。

重要: この関数はsession_write_close()エイリアスです。動作は完全に同じで、どちらを使用しても問題ありません。一般的にはsession_write_close()の方が多く使用されますが、session_commit()という名前は「トランザクションのコミット」を連想させ、より直感的です!

基本的な構文

session_commit(): bool
// または
session_write_close(): bool
  • 引数: なし
  • 戻り値: 成功時はtrue、失敗時はfalse

なぜ重要なのか?

// セッションファイルのロック問題

// 問題: セッション開始中、セッションファイルはロックされる
session_start();
$_SESSION['data'] = 'value';
// この時点で、他のリクエストは同じセッションにアクセスできない

// 長時間処理
sleep(10);  // 10秒間、他のリクエストがブロックされる

// 解決: 早めにセッションを閉じてロックを解放
session_start();
$_SESSION['data'] = 'value';
session_commit();  // ロック解放

// これ以降、他のリクエストがセッションにアクセス可能
sleep(10);  // 他のリクエストはブロックされない

基本的な使用例

シンプルな使用

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

// データを設定
$_SESSION['user_id'] = 123;
$_SESSION['username'] = 'john';

// セッションを保存して終了
session_commit();

echo "セッションが保存されました\n";

// この後、$_SESSIONは空の配列になる
echo "セッション変数の数: " . count($_SESSION) . "\n";  // 0

セッションの再開

// セッション開始
session_start();
$_SESSION['count'] = 1;
session_commit();

echo "最初のセッション終了\n";

// セッションを再開
session_start();
echo "count: {$_SESSION['count']}\n";  // 1

$_SESSION['count']++;
session_commit();

echo "2回目のセッション終了\n";

長時間処理の前にコミット

session_start();

// ユーザー情報を取得
$userId = $_SESSION['user_id'] ?? null;

// セッションを閉じる(他のリクエストのブロックを防ぐ)
session_commit();

// 長時間の処理(他のリクエストはブロックされない)
$data = performLongRunningTask($userId);

// 必要なら再度セッションを開始
session_start();
$_SESSION['task_result'] = $data;
session_commit();

function performLongRunningTask($userId) {
    sleep(5);  // 5秒の処理をシミュレート
    return "Task completed for user {$userId}";
}

読み取り専用アクセス

// セッションから読み取るだけの場合

session_start();

// データを読み取る
$username = $_SESSION['username'] ?? 'guest';
$userRole = $_SESSION['role'] ?? 'visitor';

// すぐにコミット(書き込みがないので即座に閉じる)
session_commit();

echo "User: {$username}, Role: {$userRole}\n";

// 以降の処理は他のリクエストをブロックしない
processData();

function processData() {
    // 処理...
}

実践的な使用例

例1: セッションライフサイクル管理

class SessionManager {
    private $isActive = false;
    
    /**
     * セッションを開始
     */
    public function start() {
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
            $this->isActive = true;
            return true;
        }
        return false;
    }
    
    /**
     * セッションをコミット
     */
    public function commit() {
        if ($this->isActive) {
            session_commit();
            $this->isActive = false;
            return true;
        }
        return false;
    }
    
    /**
     * データを設定してコミット
     */
    public function set($key, $value, $autoCommit = true) {
        $this->start();
        $_SESSION[$key] = $value;
        
        if ($autoCommit) {
            $this->commit();
        }
        
        return $this;
    }
    
    /**
     * データを取得
     */
    public function get($key, $default = null, $autoCommit = true) {
        $this->start();
        $value = $_SESSION[$key] ?? $default;
        
        if ($autoCommit) {
            $this->commit();
        }
        
        return $value;
    }
    
    /**
     * 複数のデータを一括設定
     */
    public function setMultiple($data) {
        $this->start();
        
        foreach ($data as $key => $value) {
            $_SESSION[$key] = $value;
        }
        
        $this->commit();
        
        return $this;
    }
    
    /**
     * セッションの状態を取得
     */
    public function isActive() {
        return $this->isActive;
    }
    
    /**
     * トランザクション的な処理
     */
    public function transaction(callable $callback) {
        $this->start();
        
        try {
            $result = $callback($_SESSION);
            $this->commit();
            return [
                'success' => true,
                'result' => $result
            ];
        } catch (Exception $e) {
            // エラー時はコミットせずに閉じる
            session_abort();
            return [
                'success' => false,
                'error' => $e->getMessage()
            ];
        }
    }
    
    /**
     * セッション情報を取得
     */
    public function getInfo() {
        $wasActive = $this->isActive;
        
        if (!$wasActive) {
            $this->start();
        }
        
        $info = [
            'session_id' => session_id(),
            'session_name' => session_name(),
            'data_count' => count($_SESSION),
            'keys' => array_keys($_SESSION)
        ];
        
        if (!$wasActive) {
            $this->commit();
        }
        
        return $info;
    }
}

// 使用例
echo "=== セッションライフサイクル管理 ===\n";

$session = new SessionManager();

// データを設定(自動コミット)
echo "データを設定:\n";
$session->set('user_id', 123);
$session->set('username', 'alice');
echo "  user_id と username を設定\n";

// データを取得(自動コミット)
echo "\nデータを取得:\n";
$userId = $session->get('user_id');
$username = $session->get('username');
echo "  user_id: {$userId}\n";
echo "  username: {$username}\n";

// 複数データを一括設定
echo "\n複数データを一括設定:\n";
$session->setMultiple([
    'email' => 'alice@example.com',
    'role' => 'admin',
    'last_login' => time()
]);
echo "  3つのデータを設定\n";

// トランザクション処理
echo "\nトランザクション処理:\n";
$result = $session->transaction(function(&$sessionData) {
    $sessionData['login_count'] = ($sessionData['login_count'] ?? 0) + 1;
    $sessionData['last_activity'] = time();
    return $sessionData['login_count'];
});

if ($result['success']) {
    echo "  成功: ログイン回数 = {$result['result']}\n";
}

// セッション情報
echo "\nセッション情報:\n";
$info = $session->getInfo();
echo "  セッションID: {$info['session_id']}\n";
echo "  データ数: {$info['data_count']}\n";
echo "  キー: " . implode(', ', $info['keys']) . "\n";

例2: 長時間処理ハンドラー

class LongRunningTaskHandler {
    private $sessionData = [];
    
    /**
     * タスクを実行(セッションブロックを最小化)
     */
    public function executeTask($taskName, callable $task) {
        $startTime = microtime(true);
        
        // セッションからデータを読み取る
        session_start();
        $this->sessionData = $_SESSION;
        $userId = $_SESSION['user_id'] ?? null;
        
        // すぐにセッションをコミット(他のリクエストのブロックを解除)
        session_commit();
        
        echo "セッションをコミットしました(ロック解放)\n";
        
        // 長時間の処理を実行
        try {
            $result = $task($userId, $this->sessionData);
            
            // 処理完了後、結果をセッションに保存
            session_start();
            $_SESSION['tasks'][$taskName] = [
                'status' => 'completed',
                'result' => $result,
                'completed_at' => time(),
                'duration' => microtime(true) - $startTime
            ];
            session_commit();
            
            return [
                'success' => true,
                'result' => $result,
                'duration' => microtime(true) - $startTime
            ];
            
        } catch (Exception $e) {
            // エラーをセッションに記録
            session_start();
            $_SESSION['tasks'][$taskName] = [
                'status' => 'failed',
                'error' => $e->getMessage(),
                'failed_at' => time()
            ];
            session_commit();
            
            return [
                'success' => false,
                'error' => $e->getMessage()
            ];
        }
    }
    
    /**
     * タスクの状態を取得
     */
    public function getTaskStatus($taskName) {
        session_start();
        $status = $_SESSION['tasks'][$taskName] ?? null;
        session_commit();
        
        return $status;
    }
    
    /**
     * すべてのタスク状態を取得
     */
    public function getAllTasks() {
        session_start();
        $tasks = $_SESSION['tasks'] ?? [];
        session_commit();
        
        return $tasks;
    }
    
    /**
     * 複数のタスクを並行実行(シミュレート)
     */
    public function executeMultipleTasks($tasks) {
        $results = [];
        
        foreach ($tasks as $taskName => $taskCallback) {
            $results[$taskName] = $this->executeTask($taskName, $taskCallback);
        }
        
        return $results;
    }
    
    /**
     * タスクをクリーンアップ
     */
    public function clearTasks() {
        session_start();
        unset($_SESSION['tasks']);
        session_commit();
    }
}

// 使用例
echo "=== 長時間処理ハンドラー ===\n";

// セッション初期化
session_start();
$_SESSION['user_id'] = 456;
$_SESSION['username'] = 'bob';
session_commit();

$handler = new LongRunningTaskHandler();

// タスク1: データ処理
echo "\nタスク1を実行中...\n";
$result1 = $handler->executeTask('data_processing', function($userId, $sessionData) {
    echo "  ユーザーID {$userId} のデータを処理中...\n";
    sleep(2);  // 2秒の処理をシミュレート
    return "Processed 1000 records";
});

echo "タスク1完了: " . ($result1['success'] ? '成功' : '失敗') . "\n";
echo "  結果: {$result1['result']}\n";
echo "  所要時間: " . round($result1['duration'], 2) . "秒\n";

// タスク2: レポート生成
echo "\nタスク2を実行中...\n";
$result2 = $handler->executeTask('report_generation', function($userId, $sessionData) {
    echo "  レポートを生成中...\n";
    sleep(3);  // 3秒の処理をシミュレート
    return "Report generated successfully";
});

echo "タスク2完了: " . ($result2['success'] ? '成功' : '失敗') . "\n";

// すべてのタスク状態を表示
echo "\n=== すべてのタスク状態 ===\n";
$allTasks = $handler->getAllTasks();
foreach ($allTasks as $taskName => $taskInfo) {
    echo "{$taskName}:\n";
    echo "  ステータス: {$taskInfo['status']}\n";
    if (isset($taskInfo['duration'])) {
        echo "  所要時間: " . round($taskInfo['duration'], 2) . "秒\n";
    }
}

例3: AJAX処理用セッションハンドラー

class AjaxSessionHandler {
    /**
     * AJAX リクエスト用にセッションを読み取り
     */
    public function readForAjax($keys = []) {
        session_start();
        
        $data = [];
        
        if (empty($keys)) {
            // すべてのセッションデータを取得
            $data = $_SESSION;
        } else {
            // 指定されたキーのみ取得
            foreach ($keys as $key) {
                $data[$key] = $_SESSION[$key] ?? null;
            }
        }
        
        // すぐにコミット(読み取りのみなので)
        session_commit();
        
        return $data;
    }
    
    /**
     * AJAX リクエスト用にセッションを更新
     */
    public function updateFromAjax($data) {
        session_start();
        
        $updated = [];
        
        foreach ($data as $key => $value) {
            $_SESSION[$key] = $value;
            $updated[] = $key;
        }
        
        // 更新後すぐにコミット
        session_commit();
        
        return [
            'success' => true,
            'updated_keys' => $updated,
            'count' => count($updated)
        ];
    }
    
    /**
     * ポーリング用にデータを取得
     */
    public function pollData($keys, $timeout = 10) {
        $startTime = time();
        $lastData = [];
        
        while ((time() - $startTime) < $timeout) {
            session_start();
            
            $currentData = [];
            foreach ($keys as $key) {
                $currentData[$key] = $_SESSION[$key] ?? null;
            }
            
            session_commit();
            
            // データが変更された場合は返す
            if ($currentData !== $lastData && !empty(array_filter($currentData))) {
                return [
                    'success' => true,
                    'data' => $currentData,
                    'changed' => true
                ];
            }
            
            $lastData = $currentData;
            usleep(500000);  // 0.5秒待機
        }
        
        return [
            'success' => true,
            'data' => $lastData,
            'changed' => false,
            'timeout' => true
        ];
    }
    
    /**
     * リアルタイム通知用の状態更新
     */
    public function updateNotificationStatus($notificationId, $status) {
        session_start();
        
        if (!isset($_SESSION['notifications'])) {
            $_SESSION['notifications'] = [];
        }
        
        $_SESSION['notifications'][$notificationId] = [
            'status' => $status,
            'timestamp' => time()
        ];
        
        session_commit();
        
        return true;
    }
    
    /**
     * 未読通知を取得
     */
    public function getUnreadNotifications() {
        session_start();
        
        $notifications = $_SESSION['notifications'] ?? [];
        $unread = array_filter($notifications, function($notification) {
            return $notification['status'] === 'unread';
        });
        
        session_commit();
        
        return [
            'count' => count($unread),
            'notifications' => $unread
        ];
    }
    
    /**
     * カート情報を取得(頻繁なAJAXアクセス用)
     */
    public function getCartSummary() {
        session_start();
        
        $cart = $_SESSION['cart'] ?? [];
        $summary = [
            'item_count' => count($cart),
            'total' => array_sum(array_column($cart, 'price')),
            'last_updated' => $_SESSION['cart_updated'] ?? null
        ];
        
        // すぐにコミット(読み取りのみ)
        session_commit();
        
        return $summary;
    }
}

// 使用例
echo "=== AJAX セッションハンドラー ===\n";

// セッション初期化
session_start();
$_SESSION['user_id'] = 789;
$_SESSION['cart'] = [
    ['id' => 1, 'name' => 'Product A', 'price' => 1000],
    ['id' => 2, 'name' => 'Product B', 'price' => 2000]
];
$_SESSION['cart_updated'] = time();
$_SESSION['notifications'] = [
    'notif1' => ['status' => 'unread', 'timestamp' => time() - 100],
    'notif2' => ['status' => 'read', 'timestamp' => time() - 200]
];
session_commit();

$ajax = new AjaxSessionHandler();

// AJAX: 特定のキーを読み取り
echo "特定のキーを読み取り:\n";
$data = $ajax->readForAjax(['user_id', 'cart']);
echo "  user_id: {$data['user_id']}\n";
echo "  カートアイテム数: " . count($data['cart']) . "\n";

// AJAX: データを更新
echo "\nデータを更新:\n";
$result = $ajax->updateFromAjax([
    'last_action' => 'view_product',
    'last_action_time' => time()
]);
echo "  更新されたキー: " . implode(', ', $result['updated_keys']) . "\n";

// AJAX: カート概要を取得
echo "\nカート概要:\n";
$cartSummary = $ajax->getCartSummary();
echo "  アイテム数: {$cartSummary['item_count']}\n";
echo "  合計: " . number_format($cartSummary['total']) . "円\n";

// AJAX: 未読通知を取得
echo "\n未読通知:\n";
$unread = $ajax->getUnreadNotifications();
echo "  未読数: {$unread['count']}\n";

例4: 読み取り専用セッションアクセス

class ReadOnlySessionAccess {
    /**
     * 読み取り専用でセッションデータを取得
     */
    public static function readOnly(callable $callback) {
        session_start();
        
        // セッションデータのコピーを作成(読み取り専用)
        $sessionCopy = $_SESSION;
        
        // すぐにコミット(他のリクエストをブロックしない)
        session_commit();
        
        // コールバックに読み取り専用データを渡す
        return $callback($sessionCopy);
    }
    
    /**
     * 特定のキーを読み取り
     */
    public static function get($key, $default = null) {
        return self::readOnly(function($session) use ($key, $default) {
            return $session[$key] ?? $default;
        });
    }
    
    /**
     * 複数のキーを読み取り
     */
    public static function getMultiple($keys) {
        return self::readOnly(function($session) use ($keys) {
            $result = [];
            foreach ($keys as $key) {
                $result[$key] = $session[$key] ?? null;
            }
            return $result;
        });
    }
    
    /**
     * セッション全体を取得
     */
    public static function getAll() {
        return self::readOnly(function($session) {
            return $session;
        });
    }
    
    /**
     * セッションに値が存在するかチェック
     */
    public static function has($key) {
        return self::readOnly(function($session) use ($key) {
            return isset($session[$key]);
        });
    }
    
    /**
     * ネストされたキーを読み取り
     */
    public static function getNested($path, $default = null) {
        return self::readOnly(function($session) use ($path, $default) {
            $keys = explode('.', $path);
            $value = $session;
            
            foreach ($keys as $key) {
                if (!isset($value[$key])) {
                    return $default;
                }
                $value = $value[$key];
            }
            
            return $value;
        });
    }
    
    /**
     * 条件に一致する値をフィルタリング
     */
    public static function filter(callable $predicate) {
        return self::readOnly(function($session) use ($predicate) {
            return array_filter($session, $predicate, ARRAY_FILTER_USE_BOTH);
        });
    }
    
    /**
     * セッションデータをマッピング
     */
    public static function map(callable $callback) {
        return self::readOnly(function($session) use ($callback) {
            return array_map($callback, $session);
        });
    }
}

// 使用例
echo "=== 読み取り専用セッションアクセス ===\n";

// セッション初期化
session_start();
$_SESSION = [
    'user' => [
        'id' => 123,
        'name' => 'Charlie',
        'email' => 'charlie@example.com'
    ],
    'settings' => [
        'theme' => 'dark',
        'language' => 'ja'
    ],
    'cart' => [
        'items' => 3,
        'total' => 5000
    ]
];
session_commit();

// 単一の値を読み取り
echo "ユーザー名: " . ReadOnlySessionAccess::get('user')['name'] . "\n";

// 複数の値を読み取り
echo "\n複数の値を読み取り:\n";
$data = ReadOnlySessionAccess::getMultiple(['user', 'settings']);
echo "  ユーザーID: {$data['user']['id']}\n";
echo "  テーマ: {$data['settings']['theme']}\n";

// ネストされた値を読み取り
echo "\nネストされた値:\n";
$theme = ReadOnlySessionAccess::getNested('settings.theme');
$userId = ReadOnlySessionAccess::getNested('user.id');
echo "  テーマ: {$theme}\n";
echo "  ユーザーID: {$userId}\n";

// 値の存在チェック
echo "\n値の存在チェック:\n";
echo "  'user' exists: " . (ReadOnlySessionAccess::has('user') ? 'Yes' : 'No') . "\n";
echo "  'admin' exists: " . (ReadOnlySessionAccess::has('admin') ? 'Yes' : 'No') . "\n";

// カスタム処理
echo "\nカスタム処理:\n";
$userInfo = ReadOnlySessionAccess::readOnly(function($session) {
    return [
        'name' => $session['user']['name'] ?? 'Unknown',
        'email' => $session['user']['email'] ?? 'No email',
        'cart_items' => $session['cart']['items'] ?? 0
    ];
});
echo "  名前: {$userInfo['name']}\n";
echo "  メール: {$userInfo['email']}\n";
echo "  カートアイテム: {$userInfo['cart_items']}\n";

例5: 同時リクエスト処理の最適化

class ConcurrentRequestHandler {
    private $lockTimeout = 30;  // 秒
    
    /**
     * セッションロックを最小化してリクエストを処理
     */
    public function handleRequest($requestType, callable $handler) {
        $startTime = microtime(true);
        
        // セッション開始
        session_start();
        $lockAcquiredAt = microtime(true);
        
        // 必要なデータを取得
        $sessionData = $_SESSION;
        
        // すぐにコミット(ロック時間を最小化)
        session_commit();
        $lockReleasedAt = microtime(true);
        
        echo "ロック時間: " . round(($lockReleasedAt - $lockAcquiredAt) * 1000, 2) . "ms\n";
        
        // メインの処理を実行(セッションロックなし)
        try {
            $result = $handler($sessionData);
            
            // 結果をセッションに保存
            if ($result['save_to_session'] ?? false) {
                session_start();
                foreach ($result['data'] ?? [] as $key => $value) {
                    $_SESSION[$key] = $value;
                }
                session_commit();
            }
            
            $totalTime = microtime(true) - $startTime;
            
            return [
                'success' => true,
                'result' => $result,
                'metrics' => [
                    'total_time' => round($totalTime * 1000, 2) . 'ms',
                    'lock_time' => round(($lockReleasedAt - $lockAcquiredAt) * 1000, 2) . 'ms',
                    'lock_percentage' => round((($lockReleasedAt - $lockAcquiredAt) / $totalTime) * 100, 2) . '%'
                ]
            ];
            
        } catch (Exception $e) {
            return [
                'success' => false,
                'error' => $e->getMessage()
            ];
        }
    }
    
    /**
     * 並行リクエストをシミュレート
     */
    public function simulateConcurrentRequests($requestCount) {
        echo "=== {$requestCount}個の並行リクエストをシミュレート ===\n";
        
        $results = [];
        
        for ($i = 1; $i <= $requestCount; $i++) {
            echo "\nリクエスト {$i}:\n";
            
            $result = $this->handleRequest("request_{$i}", function($sessionData) use ($i) {
                // 処理をシミュレート
                usleep(rand(100000, 500000));  // 100-500ms
                
                return [
                    'request_id' => $i,
                    'processed_at' => time(),
                    'save_to_session' => true,
                    'data' => [
                        "request_{$i}_result" => "completed"
                    ]
                ];
            });
            
            $results[] = $result;
            
            echo "  結果: " . ($result['success'] ? '成功' : '失敗') . "\n";
            echo "  総時間: {$result['metrics']['total_time']}\n";
            echo "  ロック時間: {$result['metrics']['lock_time']}\n";
            echo "  ロック割合: {$result['metrics']['lock_percentage']}\n";
        }
        
        return $results;
    }
    
    /**
     * ロック時間を比較(最適化前vs後)
     */
    public function comparePerformance() {
        echo "=== パフォーマンス比較 ===\n";
        
        // 最適化前(長時間ロック)
        echo "\n最適化前(セッションを長時間ロック):\n";
        $startTime = microtime(true);
        
        session_start();
        $lockStart = microtime(true);
        
        // 長時間の処理(セッションロック中)
        sleep(1);
        
        $_SESSION['test'] = 'value';
        session_commit();
        $lockEnd = microtime(true);
        
        $totalTime1 = microtime(true) - $startTime;
        $lockTime1 = $lockEnd - $lockStart;
        
        echo "  総時間: " . round($totalTime1 * 1000, 2) . "ms\n";
        echo "  ロック時間: " . round($lockTime1 * 1000, 2) . "ms\n";
        echo "  ロック割合: " . round(($lockTime1 / $totalTime1) * 100, 2) . "%\n";
        
        // 最適化後(短時間ロック)
        echo "\n最適化後(早めにコミット):\n";
        $startTime = microtime(true);
        
        session_start();
        $lockStart = microtime(true);
        $data = $_SESSION;
        session_commit();
        $lockEnd = microtime(true);
        
        // 長時間の処理(セッションロックなし)
        sleep(1);
        
        session_start();
        $_SESSION['test'] = 'value';
        session_commit();
        
        $totalTime2 = microtime(true) - $startTime;
        $lockTime2 = $lockEnd - $lockStart;
        
        echo "  総時間: " . round($totalTime2 * 1000, 2) . "ms\n";
        echo "  ロック時間: " . round($lockTime2 * 1000, 2) . "ms\n";
        echo "  ロック割合: " . round(($lockTime2 / $totalTime2) * 100, 2) . "%\n";
        
        echo "\n改善:\n";
        echo "  ロック時間削減: " . round(($lockTime1 - $lockTime2) * 1000, 2) . "ms\n";
        echo "  ロック割合削減: " . round((($lockTime1 / $totalTime1) - ($lockTime2 / $totalTime2)) * 100, 2) . "%\n";
    }
}

// 使用例
echo "=== 同時リクエスト処理最適化 ===\n";

// セッション初期化
session_start();
$_SESSION['user_id'] = 999;
session_commit();

$handler = new ConcurrentRequestHandler();

// 並行リクエストをシミュレート
$handler->simulateConcurrentRequests(3);

// パフォーマンス比較
echo "\n";
$handler->comparePerformance();

例6: セッションパフォーマンス最適化

class SessionPerformanceOptimizer {
    private $metrics = [];
    
    /**
     * セッションアクセスパターンを分析
     */
    public function analyzeAccessPattern() {
        $patterns = [
            'read_only' => 0,
            'read_write' => 0,
            'write_heavy' => 0
        ];
        
        // 10回のアクセスをシミュレート
        for ($i = 0; $i < 10; $i++) {
            $readCount = rand(1, 10);
            $writeCount = rand(0, 5);
            
            if ($writeCount === 0) {
                $patterns['read_only']++;
            } elseif ($writeCount < $readCount) {
                $patterns['read_write']++;
            } else {
                $patterns['write_heavy']++;
            }
        }
        
        return $patterns;
    }
    
    /**
     * 最適化された読み取り
     */
    public function optimizedRead($keys) {
        $startTime = microtime(true);
        
        session_start();
        $lockTime = microtime(true);
        
        $data = [];
        foreach ($keys as $key) {
            $data[$key] = $_SESSION[$key] ?? null;
        }
        
        session_commit();  // すぐにコミット
        $commitTime = microtime(true);
        
        $this->recordMetric('optimized_read', [
            'total_time' => ($commitTime - $startTime) * 1000,
            'lock_time' => ($commitTime - $lockTime) * 1000,
            'keys_read' => count($keys)
        ]);
        
        return $data;
    }
    
    /**
     * 最適化された書き込み
     */
    public function optimizedWrite($data) {
        $startTime = microtime(true);
        
        session_start();
        $lockTime = microtime(true);
        
        foreach ($data as $key => $value) {
            $_SESSION[$key] = $value;
        }
        
        session_commit();  // 書き込み後すぐにコミット
        $commitTime = microtime(true);
        
        $this->recordMetric('optimized_write', [
            'total_time' => ($commitTime - $startTime) * 1000,
            'lock_time' => ($commitTime - $lockTime) * 1000,
            'keys_written' => count($data)
        ]);
        
        return true;
    }
    
    /**
     * バッチ操作の最適化
     */
    public function batchOperation($operations) {
        $startTime = microtime(true);
        
        session_start();
        $lockTime = microtime(true);
        
        $results = [];
        
        foreach ($operations as $op) {
            if ($op['type'] === 'read') {
                $results[] = $_SESSION[$op['key']] ?? null;
            } elseif ($op['type'] === 'write') {
                $_SESSION[$op['key']] = $op['value'];
            }
        }
        
        session_commit();
        $commitTime = microtime(true);
        
        $this->recordMetric('batch_operation', [
            'total_time' => ($commitTime - $startTime) * 1000,
            'lock_time' => ($commitTime - $lockTime) * 1000,
            'operations' => count($operations)
        ]);
        
        return $results;
    }
    
    /**
     * メトリクスを記録
     */
    private function recordMetric($operation, $data) {
        if (!isset($this->metrics[$operation])) {
            $this->metrics[$operation] = [];
        }
        
        $this->metrics[$operation][] = $data;
    }
    
    /**
     * メトリクスレポートを生成
     */
    public function generateReport() {
        $report = [];
        
        foreach ($this->metrics as $operation => $metrics) {
            $totalTime = array_sum(array_column($metrics, 'total_time'));
            $lockTime = array_sum(array_column($metrics, 'lock_time'));
            $count = count($metrics);
            
            $report[$operation] = [
                'count' => $count,
                'avg_total_time' => round($totalTime / $count, 2) . 'ms',
                'avg_lock_time' => round($lockTime / $count, 2) . 'ms',
                'total_lock_time' => round($lockTime, 2) . 'ms',
                'lock_percentage' => round(($lockTime / $totalTime) * 100, 2) . '%'
            ];
        }
        
        return $report;
    }
    
    /**
     * ベストプラクティスを提案
     */
    public function suggestBestPractices($accessPattern) {
        $suggestions = [];
        
        if ($accessPattern['read_only'] > 5) {
            $suggestions[] = "読み取り専用アクセスが多いため、session_commit()を読み取り直後に呼び出してください";
        }
        
        if ($accessPattern['write_heavy'] > 3) {
            $suggestions[] = "書き込みが多いため、バッチ操作を検討してください";
        }
        
        if ($accessPattern['read_write'] > 5) {
            $suggestions[] = "読み書き混在の場合、読み取り→コミット→処理→書き込みの順序を検討してください";
        }
        
        return $suggestions;
    }
}

// 使用例
echo "=== セッションパフォーマンス最適化 ===\n";

// セッション初期化
session_start();
for ($i = 1; $i <= 20; $i++) {
    $_SESSION["key{$i}"] = "value{$i}";
}
session_commit();

$optimizer = new SessionPerformanceOptimizer();

// 最適化された読み取り
echo "最適化された読み取り:\n";
$data = $optimizer->optimizedRead(['key1', 'key2', 'key3']);
echo "  読み取りデータ: " . count($data) . "件\n";

// 最適化された書き込み
echo "\n最適化された書き込み:\n";
$optimizer->optimizedWrite([
    'new_key1' => 'new_value1',
    'new_key2' => 'new_value2'
]);
echo "  書き込み完了\n";

// バッチ操作
echo "\nバッチ操作:\n";
$operations = [
    ['type' => 'read', 'key' => 'key1'],
    ['type' => 'write', 'key' => 'batch_key', 'value' => 'batch_value'],
    ['type' => 'read', 'key' => 'key2']
];
$results = $optimizer->batchOperation($operations);
echo "  操作数: " . count($operations) . "\n";

// メトリクスレポート
echo "\n=== パフォーマンスレポート ===\n";
$report = $optimizer->generateReport();

foreach ($report as $operation => $metrics) {
    echo "\n{$operation}:\n";
    echo "  実行回数: {$metrics['count']}\n";
    echo "  平均総時間: {$metrics['avg_total_time']}\n";
    echo "  平均ロック時間: {$metrics['avg_lock_time']}\n";
    echo "  ロック割合: {$metrics['lock_percentage']}\n";
}

// アクセスパターン分析と提案
echo "\n=== ベストプラクティス提案 ===\n";
$pattern = $optimizer->analyzeAccessPattern();
echo "アクセスパターン:\n";
echo "  読み取り専用: {$pattern['read_only']}\n";
echo "  読み書き混在: {$pattern['read_write']}\n";
echo "  書き込み中心: {$pattern['write_heavy']}\n";

$suggestions = $optimizer->suggestBestPractices($pattern);
echo "\n提案:\n";
foreach ($suggestions as $suggestion) {
    echo "  - {$suggestion}\n";
}

session_write_close()との関係

// session_commit() と session_write_close() は完全に同じ

// どちらも同じ動作
session_commit();
// または
session_write_close();

// 好みの問題だが、一般的な使い分け:

// session_commit() - トランザクション的な処理を強調
session_start();
$_SESSION['data'] = 'value';
session_commit();  // "コミット"という名前が直感的

// session_write_close() - セッションの終了を強調
session_start();
$_SESSION['data'] = 'value';
session_write_close();  // "書き込んで閉じる"が明確

まとめ

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

できること:

  • セッションデータを保存
  • セッションを終了
  • セッションファイルのロックを解放
  • 並行アクセスのパフォーマンス向上

session_write_close()との関係:

  • 完全に同じ関数(エイリアス)
  • どちらを使っても動作は同一
  • session_commit()の方が直感的な名前

重要な使用場面:

  • 長時間処理の前
  • AJAX処理
  • 読み取り専用アクセス
  • 並行リクエストの処理
  • API エンドポイント

パフォーマンスへの影響:

// 悪い例: 長時間ロック
session_start();
sleep(10);  // 10秒間ロック
session_commit();

// 良い例: 短時間ロック
session_start();
$data = $_SESSION;
session_commit();  // すぐにロック解放
sleep(10);  // ロックなし

ベストプラクティス:

// 1. 読み取り後すぐにコミット
session_start();
$userId = $_SESSION['user_id'];
session_commit();  // ロック解放

// 2. 書き込み後すぐにコミット
session_start();
$_SESSION['data'] = 'value';
session_commit();  // ロック解放

// 3. 長時間処理の前後でセッション管理
session_start();
$data = $_SESSION;
session_commit();
// 長時間処理
longRunningTask($data);
// 必要なら再開
session_start();
$_SESSION['result'] = $result;
session_commit();

注意点:

  • コミット後は$_SESSIONが空になる
  • 再度アクセスするにはsession_start()が必要
  • セッションデータは保存される(破棄ではない)
  • session_destroy()とは異なる

関連関数:

  • session_start(): セッション開始
  • session_abort(): 変更を破棄
  • session_destroy(): セッション削除
  • session_status(): セッション状態確認
  • session_write_close(): session_commit()と同じ

session_commit()は、セッションファイルのロックを早期に解放することで、並行リクエストのパフォーマンスを大幅に改善できる重要な関数です。特にAJAXや長時間処理を含むアプリケーションでは必須のテクニックです!

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