[PHP]http_response_code関数完全ガイド – HTTPステータスコード制御の基本と実践

PHP

はじめに

Webアプリケーション開発において、適切なHTTPステータスコードの設定は、SEO対策、ユーザーエクスペリエンス、API設計の観点から非常に重要です。PHPのhttp_response_code関数は、レスポンスのHTTPステータスコードを簡単に制御できる便利な関数です。

この記事では、http_response_code関数の基本的な使い方から実践的な活用方法まで、具体的なコード例を交えて詳しく解説します。

http_response_code関数とは?

http_response_code関数は、PHPでHTTPレスポンスのステータスコードを設定または取得するための関数です。PHP 5.4.0以降で使用可能で、従来のheader()関数よりもシンプルで直感的にステータスコードを操作できます。

基本構文

http_response_code(int $response_code = 0): int|bool
  • パラメータなし:現在のステータスコードを取得
  • パラメータあり:指定したステータスコードを設定

基本的な使用例

現在のステータスコードを取得

<?php
// 現在のHTTPステータスコードを取得
$current_code = http_response_code();
echo "現在のステータスコード: " . $current_code;
// 出力: 現在のステータスコード: 200(デフォルト)
?>

ステータスコードの設定

<?php
// 404 Not Foundを設定
http_response_code(404);
echo "ページが見つかりません";

// 設定されたコードを確認
echo "設定されたコード: " . http_response_code();
// 出力: 設定されたコード: 404
?>

主要なHTTPステータスコード

Webアプリケーションでよく使用されるステータスコードとその用途を整理しました:

2xx 成功

<?php
// 200 OK - 正常なレスポンス
http_response_code(200);

// 201 Created - リソースの作成成功
http_response_code(201);
echo json_encode(['message' => 'ユーザーが作成されました']);

// 204 No Content - 成功だがコンテンツなし
http_response_code(204);
exit; // 204の場合は通常コンテンツを返さない
?>

3xx リダイレクト

<?php
// 301 Moved Permanently - 永続的なリダイレクト
http_response_code(301);
header('Location: https://example.com/new-page');
exit;

// 302 Found - 一時的なリダイレクト
http_response_code(302);
header('Location: https://example.com/temporary-page');
exit;

// 304 Not Modified - キャッシュ有効
http_response_code(304);
exit;
?>

4xx クライアントエラー

<?php
// 400 Bad Request - 不正なリクエスト
if (empty($_POST['required_field'])) {
    http_response_code(400);
    echo json_encode(['error' => '必須フィールドが未入力です']);
    exit;
}

// 401 Unauthorized - 認証が必要
if (!isAuthenticated()) {
    http_response_code(401);
    echo json_encode(['error' => '認証が必要です']);
    exit;
}

// 403 Forbidden - アクセス禁止
if (!hasPermission()) {
    http_response_code(403);
    echo json_encode(['error' => 'アクセス権限がありません']);
    exit;
}

// 404 Not Found - リソースが見つからない
if (!pageExists()) {
    http_response_code(404);
    include '404.php';
    exit;
}
?>

5xx サーバーエラー

<?php
// 500 Internal Server Error - サーバー内部エラー
try {
    // 何らかの処理
    processData();
} catch (Exception $e) {
    http_response_code(500);
    error_log('Server Error: ' . $e->getMessage());
    echo json_encode(['error' => 'サーバーエラーが発生しました']);
    exit;
}

// 503 Service Unavailable - サービス利用不可
if (isMaintenanceMode()) {
    http_response_code(503);
    header('Retry-After: 3600'); // 1時間後に再試行
    echo 'メンテナンス中です';
    exit;
}
?>

実践的な活用例

1. REST API の実装

<?php
class UserAPI {
    public function handleRequest() {
        $method = $_SERVER['REQUEST_METHOD'];
        $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
        
        switch ($method) {
            case 'GET':
                $this->getUser($path);
                break;
            case 'POST':
                $this->createUser();
                break;
            case 'PUT':
                $this->updateUser($path);
                break;
            case 'DELETE':
                $this->deleteUser($path);
                break;
            default:
                http_response_code(405); // Method Not Allowed
                echo json_encode(['error' => '許可されていないメソッドです']);
        }
    }
    
    private function getUser($path) {
        $userId = $this->extractUserId($path);
        
        if (!$userId) {
            http_response_code(400);
            echo json_encode(['error' => '無効なユーザーIDです']);
            return;
        }
        
        $user = $this->findUser($userId);
        
        if (!$user) {
            http_response_code(404);
            echo json_encode(['error' => 'ユーザーが見つかりません']);
            return;
        }
        
        http_response_code(200);
        echo json_encode($user);
    }
    
    private function createUser() {
        $input = json_decode(file_get_contents('php://input'), true);
        
        if (!$this->validateUserData($input)) {
            http_response_code(400);
            echo json_encode(['error' => '入力データが無効です']);
            return;
        }
        
        if ($this->userExists($input['email'])) {
            http_response_code(409); // Conflict
            echo json_encode(['error' => 'ユーザーは既に存在します']);
            return;
        }
        
        $userId = $this->saveUser($input);
        
        http_response_code(201);
        header('Location: /api/users/' . $userId);
        echo json_encode(['id' => $userId, 'message' => 'ユーザーが作成されました']);
    }
    
    private function updateUser($path) {
        $userId = $this->extractUserId($path);
        $input = json_decode(file_get_contents('php://input'), true);
        
        if (!$this->findUser($userId)) {
            http_response_code(404);
            echo json_encode(['error' => 'ユーザーが見つかりません']);
            return;
        }
        
        $this->updateUserData($userId, $input);
        
        http_response_code(200);
        echo json_encode(['message' => 'ユーザーが更新されました']);
    }
    
    private function deleteUser($path) {
        $userId = $this->extractUserId($path);
        
        if (!$this->findUser($userId)) {
            http_response_code(404);
            echo json_encode(['error' => 'ユーザーが見つかりません']);
            return;
        }
        
        $this->removeUser($userId);
        
        http_response_code(204); // No Content
        // 204の場合はレスポンスボディを返さない
    }
}

$api = new UserAPI();
$api->handleRequest();
?>

2. ファイルダウンロード機能

<?php
class FileDownloader {
    public function download($filename) {
        $filepath = $this->getSecureFilePath($filename);
        
        // ファイルの存在確認
        if (!file_exists($filepath)) {
            http_response_code(404);
            echo 'ファイルが見つかりません';
            return;
        }
        
        // アクセス権限の確認
        if (!$this->hasDownloadPermission($filename)) {
            http_response_code(403);
            echo 'ダウンロード権限がありません';
            return;
        }
        
        // ファイルサイズの確認
        $filesize = filesize($filepath);
        if ($filesize === false || $filesize > 100 * 1024 * 1024) { // 100MB制限
            http_response_code(413); // Payload Too Large
            echo 'ファイルサイズが大きすぎます';
            return;
        }
        
        // 成功時のレスポンス
        http_response_code(200);
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="' . basename($filename) . '"');
        header('Content-Length: ' . $filesize);
        header('Cache-Control: no-cache');
        
        readfile($filepath);
    }
    
    private function getSecureFilePath($filename) {
        // セキュリティ: パストラバーサル攻撃を防ぐ
        $safe_filename = basename($filename);
        return '/secure/downloads/' . $safe_filename;
    }
    
    private function hasDownloadPermission($filename) {
        // 権限チェックのロジック
        return isset($_SESSION['user_id']) && $this->userCanDownload($_SESSION['user_id'], $filename);
    }
}

$downloader = new FileDownloader();
$downloader->download($_GET['file'] ?? '');
?>

3. 認証システムの実装

<?php
class AuthSystem {
    public function login($email, $password) {
        // 入力値の検証
        if (empty($email) || empty($password)) {
            http_response_code(400);
            return ['error' => 'メールアドレスとパスワードが必要です'];
        }
        
        // レート制限のチェック
        if ($this->isRateLimited($email)) {
            http_response_code(429); // Too Many Requests
            return ['error' => 'ログイン試行回数が上限に達しました。しばらく待ってからお試しください'];
        }
        
        // ユーザーの認証
        $user = $this->authenticateUser($email, $password);
        
        if (!$user) {
            $this->recordFailedAttempt($email);
            http_response_code(401);
            return ['error' => 'メールアドレスまたはパスワードが正しくありません'];
        }
        
        // アカウントの状態確認
        if (!$user['is_active']) {
            http_response_code(403);
            return ['error' => 'アカウントが無効化されています'];
        }
        
        // セッションの開始
        $this->startUserSession($user);
        
        http_response_code(200);
        return ['message' => 'ログインに成功しました', 'user' => $user];
    }
    
    public function logout() {
        if (!isset($_SESSION['user_id'])) {
            http_response_code(400);
            return ['error' => 'ログインしていません'];
        }
        
        $this->endUserSession();
        
        http_response_code(200);
        return ['message' => 'ログアウトしました'];
    }
    
    public function requireAuth() {
        if (!isset($_SESSION['user_id'])) {
            http_response_code(401);
            header('WWW-Authenticate: Bearer');
            echo json_encode(['error' => '認証が必要です']);
            exit;
        }
    }
    
    private function isRateLimited($email) {
        // レート制限のロジック(Redis等を使用)
        return false; // 実装例では省略
    }
    
    private function recordFailedAttempt($email) {
        // 失敗試行の記録
    }
}

// 使用例
session_start();
$auth = new AuthSystem();

if ($_POST['action'] === 'login') {
    $result = $auth->login($_POST['email'], $_POST['password']);
    echo json_encode($result);
} elseif ($_POST['action'] === 'logout') {
    $result = $auth->logout();
    echo json_encode($result);
}
?>

4. エラーハンドリングシステム

<?php
class ErrorHandler {
    public function handleError($error_type, $message = '') {
        switch ($error_type) {
            case 'validation':
                http_response_code(400);
                $this->sendErrorResponse('入力データが無効です', $message);
                break;
                
            case 'authentication':
                http_response_code(401);
                $this->sendErrorResponse('認証が必要です', $message);
                break;
                
            case 'permission':
                http_response_code(403);
                $this->sendErrorResponse('アクセス権限がありません', $message);
                break;
                
            case 'not_found':
                http_response_code(404);
                $this->sendErrorResponse('リソースが見つかりません', $message);
                break;
                
            case 'conflict':
                http_response_code(409);
                $this->sendErrorResponse('競合が発生しました', $message);
                break;
                
            case 'rate_limit':
                http_response_code(429);
                header('Retry-After: 60');
                $this->sendErrorResponse('リクエスト制限に達しました', $message);
                break;
                
            case 'server_error':
            default:
                http_response_code(500);
                error_log('Server Error: ' . $message);
                $this->sendErrorResponse('サーバーエラーが発生しました');
                break;
        }
    }
    
    private function sendErrorResponse($default_message, $custom_message = '') {
        $response = [
            'error' => true,
            'message' => $custom_message ?: $default_message,
            'timestamp' => date('c'),
            'status_code' => http_response_code()
        ];
        
        header('Content-Type: application/json');
        echo json_encode($response);
        exit;
    }
}

// グローバルエラーハンドラーの設定
set_exception_handler(function($exception) {
    $handler = new ErrorHandler();
    $handler->handleError('server_error', $exception->getMessage());
});

// 使用例
try {
    $user = findUser($user_id);
    if (!$user) {
        $handler = new ErrorHandler();
        $handler->handleError('not_found', 'ユーザーID: ' . $user_id);
    }
} catch (Exception $e) {
    throw $e; // グローバルハンドラーで処理される
}
?>

SEO対策での活用

1. 適切なリダイレクト処理

<?php
class SEORedirector {
    public function permanentRedirect($old_url, $new_url) {
        if ($_SERVER['REQUEST_URI'] === $old_url) {
            http_response_code(301); // SEOの評価を引き継ぐ
            header('Location: ' . $new_url);
            exit;
        }
    }
    
    public function temporaryRedirect($url) {
        http_response_code(302); // 一時的なリダイレクト
        header('Location: ' . $url);
        exit;
    }
    
    public function handleCanonicalURL() {
        $canonical_url = $this->getCanonicalURL();
        $current_url = $this->getCurrentURL();
        
        if ($canonical_url !== $current_url) {
            http_response_code(301);
            header('Location: ' . $canonical_url);
            exit;
        }
    }
}

$redirector = new SEORedirector();
$redirector->permanentRedirect('/old-page', '/new-page');
?>

2. カスタム404ページ

<?php
function handle404() {
    http_response_code(404);
    
    // SEOに配慮したカスタム404ページ
    include 'templates/404.php';
    
    // 404エラーをログに記録
    error_log('404 Error: ' . $_SERVER['REQUEST_URI'] . ' from ' . $_SERVER['HTTP_REFERER']);
    
    exit;
}

// ページの存在確認
if (!pageExists($_SERVER['REQUEST_URI'])) {
    handle404();
}
?>

header()関数との比較

<?php
// 従来のheader()関数
header("HTTP/1.1 404 Not Found");

// http_response_code()関数(推奨)
http_response_code(404);

// 組み合わせて使用
http_response_code(404);
header('Content-Type: application/json');
echo json_encode(['error' => 'Not Found']);
?>

メリット

  1. シンプルな構文:ステータスコードの設定が直感的
  2. 型安全:整数値のみ受け付ける
  3. 取得機能:現在のステータスコードを簡単に取得可能
  4. 保守性:コードの意図が明確

パフォーマンスとベストプラクティス

1. 早期終了パターン

<?php
// 認証チェックを早期に実行
if (!isAuthenticated()) {
    http_response_code(401);
    echo json_encode(['error' => 'Unauthorized']);
    exit; // 早期終了でパフォーマンス向上
}

// 以降の処理は認証済みユーザーのみ実行される
?>

2. キャッシュ制御

<?php
function setCacheHeaders($max_age = 3600) {
    http_response_code(200);
    header('Cache-Control: public, max-age=' . $max_age);
    header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $max_age) . ' GMT');
}

function setNoCacheHeaders() {
    http_response_code(200);
    header('Cache-Control: no-cache, no-store, must-revalidate');
    header('Pragma: no-cache');
    header('Expires: 0');
}

// 静的コンテンツ
setCacheHeaders(86400); // 24時間

// 動的コンテンツ
setNoCacheHeaders();
?>

まとめ

http_response_code関数は、PHPでHTTPステータスコードを効率的に管理するための重要なツールです。適切なステータスコードの設定により、以下の効果が期待できます:

主な利点

  1. SEO効果の向上:検索エンジンへの正確な情報伝達
  2. ユーザーエクスペリエンスの改善:明確なエラーメッセージと適切な処理
  3. API設計の品質向上:RESTful APIの標準に準拠
  4. デバッグの効率化:問題の特定と解決の迅速化
  5. セキュリティの強化:適切なアクセス制御とエラーハンドリング

ベストプラクティス

  • ステータスコードの意味を正しく理解して使用する
  • エラー時は適切なメッセージと共にコードを設定する
  • SEO対策を考慮したリダイレクト処理を実装する
  • パフォーマンスを意識した早期終了パターンを活用する
  • ログ記録と組み合わせて問題の追跡を行う

適切なHTTPステータスコードの使用は、プロフェッショナルなWebアプリケーション開発の基本です。http_response_code関数を効果的に活用して、ユーザーにとって使いやすく、検索エンジンにとって理解しやすいWebサイトを構築しましょう。


この記事がお役に立ちましたら、ぜひ開発チームと共有してください。HTTPステータスコードやWeb開発について、他にもご質問がございましたらお気軽にお尋ねください。

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