# 機能設計書 67-PsrHttpMessageBridge

## 概要

本ドキュメントは、Symfony PsrHttpMessage Bridgeの機能設計書である。PSR-7/PSR-17 HTTP MessageインターフェースとSymfony HttpFoundationの相互変換を提供するブリッジコンポーネントである。

### 本機能の処理概要

PsrHttpMessage Bridgeは、PSR-7（HTTP Message Interface）のServerRequestInterface/ResponseInterfaceとSymfony HttpFoundationのRequest/Responseオブジェクトを双方向に変換する。

**業務上の目的・背景**：PSR-7はPHPにおけるHTTPメッセージの標準インターフェースであり、多くのサードパーティライブラリがPSR-7を前提としている。本ブリッジにより、SymfonyアプリケーションでPSR-7ライブラリを活用できる。

**機能の利用シーン**：
- PSR-7/PSR-15ミドルウェアをSymfonyアプリケーションで使用する場合
- PSR-7を前提としたサードパーティライブラリとの統合
- Symfony RequestをPSR-7 ServerRequestに変換してPSR-15ミドルウェアに渡す
- PSR-7 ResponseをSymfony Responseに変換してSymfonyカーネルに返す

**主要な処理内容**：
1. HttpFoundationFactory - PSR-7 → Symfony HttpFoundation変換
2. PsrHttpFactory - Symfony HttpFoundation → PSR-7変換
3. UploadedFile - PSR-7アップロードファイルのSymfonyラッパー
4. ストリーミングレスポンス対応（StreamedResponse）

**関連システム・外部連携**：PSR-7/PSR-17実装ライブラリ（Nyholm/psr7、php-http/discovery等）、Symfony HttpFoundation

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面関連なし（バックエンドコンポーネント） |

## 機能種別

フレームワーク統合 / HTTPメッセージ変換

## 入力仕様

### 入力パラメータ（HttpFoundationFactory::createRequest）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| psrRequest | ServerRequestInterface | Yes | PSR-7サーバーリクエスト | - |
| streamed | bool | No | ストリーミングモード | デフォルト: false |

### 入力パラメータ（HttpFoundationFactory::createResponse）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| psrResponse | ResponseInterface | Yes | PSR-7レスポンス | - |
| streamed | bool | No | ストリーミングモード | デフォルト: false |

### 入力パラメータ（PsrHttpFactory::createRequest）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| symfonyRequest | Request | Yes | Symfony HTTPリクエスト | - |

### 入力パラメータ（PsrHttpFactory::createResponse）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| symfonyResponse | Response | Yes | Symfony HTTPレスポンス | - |

### 入力データソース

HTTPリクエスト/レスポンスオブジェクト

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Symfony Request | Request | 変換されたSymfonyリクエスト |
| Symfony Response | Response/StreamedResponse | 変換されたSymfonyレスポンス |
| PSR-7 ServerRequest | ServerRequestInterface | 変換されたPSR-7リクエスト |
| PSR-7 Response | ResponseInterface | 変換されたPSR-7レスポンス |

### 出力先

呼び出し元のPHPコード（メモリ上のオブジェクト）

## 処理フロー

### 処理シーケンス（PSR-7 → Symfony）

```
1. HttpFoundationFactory::createRequest($psrRequest)
   └─ URI解析 → サーバーパラメータ構築 → new Request(query, post, attrs, cookies, files, server, body)
2. ファイルアップロード変換
   └─ UploadedFileInterface → UploadedFile ラッパー
3. ヘッダー移行
   └─ PSR-7ヘッダー → Symfony headersに追加
```

### フローチャート

```mermaid
flowchart TD
    A{変換方向}
    A -->|PSR-7 → Symfony| B[HttpFoundationFactory]
    A -->|Symfony → PSR-7| C[PsrHttpFactory]

    B --> D[createRequest]
    D --> E[URI解析: host, port, path, query]
    E --> F[サーバーパラメータ構築]
    F --> G{streamed?}
    G -->|Yes| H[body->detach でストリーム取得]
    G -->|No| I[body->__toString でコンテンツ取得]
    H --> J[new Request]
    I --> J
    J --> K[ヘッダー追加]

    B --> L[createResponse]
    L --> M{streamed?}
    M -->|Yes| N[StreamedResponse]
    M -->|No| O[Response]
    N --> P[Set-Cookieヘッダー処理]
    O --> P

    C --> Q[createRequest]
    Q --> R[ServerRequestFactory::createServerRequest]
    R --> S[ヘッダー/ボディ/ファイル/Cookie/Query/Attributes設定]

    C --> T[createResponse]
    T --> U{レスポンス種別}
    U -->|BinaryFileResponse| V[createStreamFromFile]
    U -->|StreamedResponse| W[ob_start → sendContent → stream.write]
    U -->|通常| X[stream.write(getContent)]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | PSR-17自動検出 | PsrHttpFactory生成時にNyholm/psr7またはphp-http/discoveryを自動検出 | PSR-17ファクトリ未指定時 |
| BR-02 | Set-Cookieヘッダー分離 | createResponse時にSet-CookieヘッダーをCookieオブジェクトに変換 | PSR-7→Symfony Response変換時 |
| BR-03 | JSON ParsedBody | Content-Typeがjsonの場合、json_decode結果をparsedBodyに設定 | Symfony→PSR-7 Request変換時 |
| BR-04 | ストリーミングレスポンス | streamed=true時はStreamedResponseを使用しメモリ効率を向上 | Response変換時 |
| BR-05 | responseBufferMaxLength | ストリーミング時の読み取りバッファサイズ（デフォルト16372バイト） | StreamedResponse時 |

### 計算ロジック

特になし。

## データベース操作仕様

データベース操作は行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | LogicException | PSR-17ファクトリが見つからない | composer require php-http/discovery psr/http-factory-implementation:* |
| - | InvalidArgumentException | 無効なヘッダー名/値 | 無視して続行（try-catch） |

### リトライ仕様

該当なし。

## トランザクション仕様

該当なし。

## パフォーマンス要件

- ストリーミングモード（streamed=true）により大きなボディのメモリ使用を最小化
- responseBufferMaxLength（デフォルト16372）でストリーミング出力の粒度を制御

## セキュリティ考慮事項

- 無効なHTTPヘッダーは安全にスキップ（InvalidArgumentExceptionをcatch、PsrHttpFactory 82-85行目）

## 備考

- PSR-17 FactoryInterfaceが必須。Nyholm/psr7またはphp-http/discoveryの自動検出をサポート
- BinaryFileResponseのContent-Rangeヘッダーが無い場合はcreateStreamFromFileで直接ファイルストリーム

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: インターフェースを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | HttpFoundationFactoryInterface.php | `src/Symfony/Bridge/PsrHttpMessage/HttpFoundationFactoryInterface.php` | PSR-7→Symfony変換IF（createRequest, createResponse） |
| 1-2 | HttpMessageFactoryInterface.php | `src/Symfony/Bridge/PsrHttpMessage/HttpMessageFactoryInterface.php` | Symfony→PSR-7変換IF（createRequest, createResponse） |

#### Step 2: PSR-7 → Symfony変換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | HttpFoundationFactory.php | `src/Symfony/Bridge/PsrHttpMessage/Factory/HttpFoundationFactory.php` | PSR-7→Symfony変換実装 |
| 2-2 | UploadedFile.php | `src/Symfony/Bridge/PsrHttpMessage/Factory/UploadedFile.php` | PSR-7 UploadedFileラッパー |

**主要処理フロー（HttpFoundationFactory）**:
- **37-74行目**: createRequest()。URI解析→サーバーパラメータ構築→new Request→ヘッダー追加
- **42-43行目**: SERVER_NAME, SERVER_PORT抽出
- **51-53行目**: HTTPS判定
- **67-69行目**: streamed=trueならbody->detach()、falseならbody->__toString()
- **110-136行目**: createResponse()。Set-Cookieヘッダー分離→StreamedResponse or Response
- **138-155行目**: createStreamedResponseCallback()。seekable/readable判定、バッファサイズ指定読み取り

#### Step 3: Symfony → PSR-7変換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | PsrHttpFactory.php | `src/Symfony/Bridge/PsrHttpMessage/Factory/PsrHttpFactory.php` | Symfony→PSR-7変換実装 |

**主要処理フロー（PsrHttpFactory）**:
- **43-66行目**: コンストラクタ。PSR-17ファクトリの自動検出（Nyholm/psr7 or php-http/discovery）
- **68-113行目**: createRequest()。URI構築→ServerRequestFactory→ヘッダー/ボディ/ファイル/Cookie/Query設定
- **90-98行目**: JSON Content-Typeの場合はjson_decodeでparsedBody設定
- **153-203行目**: createResponse()。BinaryFileResponse/StreamedResponse/通常Responseの分岐処理
- **163-174行目**: StreamedResponse時はob_start()でコンテンツをキャプチャしてstream.write()

### プログラム呼び出し階層図

```
PsrHttpMessage Bridge
    │
    ├─ HttpFoundationFactory (PSR-7 → Symfony)
    │      ├─ createRequest(ServerRequestInterface)
    │      │      ├─ URI::getHost/Port/Path/Query/Scheme
    │      │      ├─ getFiles() → UploadedFile ラッパー
    │      │      └─ new Request(query, post, attrs, cookies, files, server, content)
    │      │
    │      └─ createResponse(ResponseInterface)
    │             ├─ Set-Cookie分離 → Cookie::fromString()
    │             └─ new Response / StreamedResponse
    │
    └─ PsrHttpFactory (Symfony → PSR-7)
           ├─ createRequest(Request)
           │      ├─ ServerRequestFactory::createServerRequest()
           │      ├─ withHeader/withBody/withUploadedFiles/...
           │      └─ JSON parsedBody処理
           │
           └─ createResponse(Response)
                  ├─ ResponseFactory::createResponse()
                  ├─ BinaryFileResponse → createStreamFromFile()
                  ├─ StreamedResponse → ob_start() capture
                  └─ 通常Response → stream.write(getContent())
```

### データフロー図

```
[PSR-7]                                              [Symfony HttpFoundation]

ServerRequestInterface  ──(HttpFoundationFactory)──▶  Request
ResponseInterface       ──(HttpFoundationFactory)──▶  Response / StreamedResponse

Request                ──(PsrHttpFactory)──────────▶  ServerRequestInterface
Response               ──(PsrHttpFactory)──────────▶  ResponseInterface
BinaryFileResponse     ──(PsrHttpFactory)──────────▶  ResponseInterface (file stream)
StreamedResponse       ──(PsrHttpFactory)──────────▶  ResponseInterface (captured stream)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| HttpFoundationFactoryInterface.php | `src/Symfony/Bridge/PsrHttpMessage/HttpFoundationFactoryInterface.php` | ソース | PSR-7→Symfony IF |
| HttpMessageFactoryInterface.php | `src/Symfony/Bridge/PsrHttpMessage/HttpMessageFactoryInterface.php` | ソース | Symfony→PSR-7 IF |
| HttpFoundationFactory.php | `src/Symfony/Bridge/PsrHttpMessage/Factory/HttpFoundationFactory.php` | ソース | PSR-7→Symfony実装 |
| PsrHttpFactory.php | `src/Symfony/Bridge/PsrHttpMessage/Factory/PsrHttpFactory.php` | ソース | Symfony→PSR-7実装 |
| UploadedFile.php | `src/Symfony/Bridge/PsrHttpMessage/Factory/UploadedFile.php` | ソース | アップロードファイルラッパー |
