# 機能設計書 13-ErrorHandler

## 概要

本ドキュメントは、Symfony ErrorHandlerコンポーネントの機能設計を記述する。ErrorHandlerは、PHPコードのエラー管理とデバッグを容易にするツールを提供し、例外ハンドラ、エラーハンドラ、デバッグクラスローダー等の機能を統合する。

### 本機能の処理概要

**業務上の目的・背景**：PHPのネイティブなエラー処理機構は、エラーレベルの制御が複雑であり、開発時のデバッグ情報が不十分な場合がある。ErrorHandlerコンポーネントは、PHPエラーを構造的にキャッチし、ログ出力、例外変換、詳細なスタックトレース表示を一元管理することで、開発効率とアプリケーション品質を向上させる。

**機能の利用シーン**：アプリケーションのブートストラップ時にグローバルエラーハンドラとして登録される場面、開発環境でのデバッグ詳細画面表示、本番環境でのエラーログ出力、フェータルエラーのシャットダウン時ハンドリングに利用される。

**主要な処理内容**：
1. PHPエラーのキャッチと例外変換（handleError）
2. 未キャッチ例外のハンドリング（handleException）
3. フェータルエラーのシャットダウン時処理（handleFatalError）
4. エラーレベル別のPSR-3ロガー設定（setDefaultLogger、setLoggers）
5. HTML/CLIエラーレンダリング（HtmlErrorRenderer、CliErrorRenderer）
6. エラーメッセージの強化（ClassNotFoundErrorEnhancer等）
7. デバッグクラスローダー（DebugClassLoader）

**関連システム・外部連携**：HttpKernel（ErrorController、ErrorListener）、PSR-3ロガー（Monolog等）、WebProfilerBundle（例外情報パネル）と連携する。

**権限による制御**：debugモードに応じてエラー表示の詳細度が変化する。本番環境では例外詳細は表示されず、一般的なエラーメッセージのみ表示される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 15 | 例外情報パネル | 主機能 | HtmlErrorRendererによる例外スタックトレースのレンダリング |
| 38 | 例外詳細画面（開発環境） | 主機能 | HtmlErrorRendererによる例外詳細のHTML出力 |
| 39 | エラー画面（本番環境） | 主機能 | HtmlErrorRendererによるシンプルなエラーメッセージのHTML出力 |
| 40 | エラープレビュー | 主機能 | ErrorRendererを使用したエラーページのレンダリング |

## 機能種別

エラーハンドリング / デバッグ / ログ出力

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| type | int | Yes | PHPエラーレベル（E_ERROR、E_WARNING等） | E_*定数の有効値 |
| message | string | Yes | エラーメッセージ | 文字列 |
| file | string | Yes | エラー発生ファイルパス | 文字列 |
| line | int | Yes | エラー発生行番号 | 正の整数 |
| exception | \Throwable | Yes | 未キャッチ例外オブジェクト | Throwable実装 |
| debug | bool | No | デバッグモードフラグ | boolean値 |

### 入力データソース

PHPエンジンからのエラー通知（set_error_handler経由）、未キャッチ例外（set_exception_handler経由）、シャットダウン関数（register_shutdown_function経由）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| HTML出力 | string | HtmlErrorRendererによるエラーページHTML |
| CLI出力 | string | CliErrorRendererによるコンソール向けエラー出力 |
| ログ出力 | void | PSR-3ロガーへのエラーログ出力 |
| 例外 | \ErrorException | thrownErrorsに該当する場合にスローされる例外 |

### 出力先

標準出力/標準エラー出力（CLI）、HTTPレスポンスボディ（HTML）、PSR-3ロガー

## 処理フロー

### 処理シーケンス

```
1. エラーハンドラ登録（ErrorHandler::register）
   └─ 予約メモリ確保、set_error_handler、set_exception_handlerの設定
2. PHPエラーキャッチ（handleError）
   └─ エラーレベルの判定、thrownErrors/loggedErrorsとの照合
3. 例外変換判定
   └─ thrownErrorsに該当すればErrorExceptionをスロー
4. ログ出力判定
   └─ loggedErrorsに該当すればPSR-3ロガーで出力
5. 例外ハンドリング（handleException）
   └─ ログ出力、エラー強化、例外ハンドラ呼び出し
6. フェータルエラー処理（handleFatalError）
   └─ シャットダウン時にerror_get_lastで致命的エラーを検出し処理
```

### フローチャート

```mermaid
flowchart TD
    A[PHPエラー発生] --> B[handleError]
    B --> C{thrownErrorsに該当?}
    C -->|Yes| D[ErrorException生成・スロー]
    C -->|No| E{loggedErrorsに該当?}
    E -->|Yes| F[PSR-3ロガーで出力]
    E -->|No| G[false返却]

    H[未キャッチ例外] --> I[handleException]
    I --> J[ログ出力]
    J --> K[enhanceError]
    K --> L{例外ハンドラ設定あり?}
    L -->|Yes| M[例外ハンドラ呼び出し]
    L -->|No| N[renderException]

    O[シャットダウン] --> P[handleFatalError]
    P --> Q{致命的エラー?}
    Q -->|Yes| R[FatalError/OutOfMemoryError生成]
    R --> I
    Q -->|No| S[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-13-01 | エラーレベル制御 | thrownErrors、loggedErrors、scopedErrors、tracedErrors、screamedErrorsの5つのビットフィールドでエラーハンドリングを制御 | handleError実行時 |
| BR-13-02 | デプリケーション非スロー | E_DEPRECATEDとE_USER_DEPRECATEDはthrowAt設定に関わらずスローされない | handleError実行時 |
| BR-13-03 | ユーザーエラー必スロー | E_RECOVERABLE_ERRORとE_USER_ERRORは常にスローされる | handleError実行時 |
| BR-13-04 | 予約メモリ | OutOfMemoryError対応として32KBの予約メモリを確保 | register実行時 |
| BR-13-05 | サイレンスエラーキャッシュ | @抑制されたエラーは100件までキャッシュし、重複を集約 | handleError実行時 |

### 計算ロジック

エラーレベルのビット演算: thrownErrors、loggedErrors等はビットフィールドとして管理され、PHPのE_*定数との論理積（&）で処理対象を判定する。error_reporting()の戻り値とscreamedErrorsの論理和で最終的な処理対象レベルを決定する。

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

### 操作別データベース影響一覧

本コンポーネントはデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ErrorException | thrownErrorsに該当するPHPエラー | try-catchで捕捉 |
| - | FatalError | E_ERROR等の致命的エラー | シャットダウンハンドラで処理 |
| - | OutOfMemoryError | メモリ不足エラー | 予約メモリを解放して最低限の処理を実行 |

### リトライ仕様

リトライは行わない。エラーハンドラの再帰呼び出し防止（isRecursiveフラグ）を実装。

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

トランザクション管理は行わない。

## パフォーマンス要件

- エラーハンドリングはPHPエンジンレベルで設定されるため、低オーバーヘッドであることが重要
- サイレンスエラーキャッシュは100件でクリアされ、メモリ使用量を制限
- デバッグモードでのスタックトレース取得はdebug_backtrace使用による一定のオーバーヘッドあり

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

- 本番環境（debug=false）では例外の詳細情報（ファイルパス、スタックトレース等）がユーザーに表示されない
- HtmlErrorRendererはデバッグモードに応じてX-Debug-Exceptionヘッダーの出力を制御
- 匿名クラスのパース処理でファイルパスの漏洩を防止（parseAnonymousClass）

## 備考

- ErrorEnhancerにより、ClassNotFound、UndefinedFunction、UndefinedMethodの各エラーに対して候補の提案を行う
- DebugClassLoaderはクラスのオートロード時に非推奨チェックやインターフェース整合性チェックを行う
- BufferingLoggerはブートストラップ中のログを一時保持し、本番ロガー設定後にフラッシュする

---

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

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

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | FlattenException.php | `src/Symfony/Component/ErrorHandler/Exception/FlattenException.php` | エラー情報のシリアライズ可能な表現。シリアライズ不可能な例外をフラット化 |
| 1-2 | SilencedErrorContext.php | `src/Symfony/Component/ErrorHandler/Exception/SilencedErrorContext.php` | @抑制エラーの情報を保持するコンテキストクラス |
| 1-3 | ErrorRendererInterface.php | `src/Symfony/Component/ErrorHandler/ErrorRenderer/ErrorRendererInterface.php` | エラーレンダラーのインターフェース |

**読解のコツ**: ErrorHandlerの5つのビットフィールド（thrownErrors、loggedErrors、scopedErrors、tracedErrors、screamedErrors）の関係を理解することが最重要。各フィールドはE_*定数の論理和で構成される。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ErrorHandler.php | `src/Symfony/Component/ErrorHandler/ErrorHandler.php` | 中核クラス。register、handleError、handleException、handleFatalErrorの4つの主要メソッド |

**主要処理フロー**:
1. **108-154行目**: register()。予約メモリ確保、set_error_handler/set_exception_handler設定、既存ハンドラとの連携
2. **383-466行目**: handleError()。エラーレベル判定、サイレンスエラー処理、ErrorException変換、ログ出力
3. **473-539行目**: handleException()。ログ出力、enhanceError、例外ハンドラ呼び出し
4. **548-622行目**: handleFatalError()。シャットダウン時処理、FatalError/OutOfMemoryError生成
5. **630-645行目**: renderException()。CLI/HTML判定によるエラーレンダリング

#### Step 3: エラーレンダリング層

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | HtmlErrorRenderer.php | `src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php` | HTML形式のエラーページ生成。debugモード別の出力制御 |
| 3-2 | CliErrorRenderer.php | `src/Symfony/Component/ErrorHandler/ErrorRenderer/CliErrorRenderer.php` | CLI形式のエラー出力 |

**主要処理フロー**:
- **62-73行目（HtmlErrorRenderer）**: render()。FlattenException生成、デバッグモード別ヘッダー設定

#### Step 4: エラー強化

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ClassNotFoundErrorEnhancer.php | `src/Symfony/Component/ErrorHandler/ErrorEnhancer/ClassNotFoundErrorEnhancer.php` | クラス未検出エラーの候補提案 |
| 4-2 | UndefinedFunctionErrorEnhancer.php | `src/Symfony/Component/ErrorHandler/ErrorEnhancer/UndefinedFunctionErrorEnhancer.php` | 未定義関数エラーの候補提案 |
| 4-3 | UndefinedMethodErrorEnhancer.php | `src/Symfony/Component/ErrorHandler/ErrorEnhancer/UndefinedMethodErrorEnhancer.php` | 未定義メソッドエラーの候補提案 |

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

```
ErrorHandler::register()
    │
    ├─ set_error_handler([handler, 'handleError'])
    ├─ set_exception_handler([handler, 'handleException'])
    └─ register_shutdown_function(handleFatalError)

ErrorHandler::handleError()
    │
    ├─ ビットフィールド判定
    │      ├─ thrownErrors → ErrorException::throw
    │      └─ loggedErrors → LoggerInterface::log
    │
    └─ cleanTrace()（スタックトレース整理）

ErrorHandler::handleException()
    │
    ├─ LoggerInterface::log()
    ├─ enhanceError()
    │      └─ ErrorEnhancerInterface::enhance()
    └─ exceptionHandler()
           └─ renderException()
                  ├─ HtmlErrorRenderer::render()
                  └─ CliErrorRenderer::render()

ErrorHandler::handleFatalError()
    │
    └─ FatalError / OutOfMemoryError
           └─ handleException()
```

### データフロー図

```
[入力]                    [処理]                           [出力]

PHPエラー            ───▶ handleError()               ───▶ ErrorException / ログ出力
(set_error_handler)        │
                           ├─ ビットフィールド判定
                           └─ レベル別処理

未キャッチ例外       ───▶ handleException()            ───▶ HTML/CLIエラー出力
(set_exception_handler)    │
                           ├─ ログ出力
                           ├─ エラー強化
                           └─ レンダリング

致命的エラー         ───▶ handleFatalError()           ───▶ FatalError → handleException
(register_shutdown)        │
                           └─ error_get_last()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ErrorHandler.php | `src/Symfony/Component/ErrorHandler/ErrorHandler.php` | ソース | エラーハンドラ中核クラス |
| Debug.php | `src/Symfony/Component/ErrorHandler/Debug.php` | ソース | デバッグモード有効化ヘルパー |
| DebugClassLoader.php | `src/Symfony/Component/ErrorHandler/DebugClassLoader.php` | ソース | デバッグ用クラスローダー |
| BufferingLogger.php | `src/Symfony/Component/ErrorHandler/BufferingLogger.php` | ソース | ブートストラップ用バッファリングロガー |
| HtmlErrorRenderer.php | `src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php` | ソース | HTMLエラーレンダラー |
| CliErrorRenderer.php | `src/Symfony/Component/ErrorHandler/ErrorRenderer/CliErrorRenderer.php` | ソース | CLIエラーレンダラー |
| FlattenException.php | `src/Symfony/Component/ErrorHandler/Exception/FlattenException.php` | ソース | フラット化例外 |
| FatalError.php | `src/Symfony/Component/ErrorHandler/Error/FatalError.php` | ソース | 致命的エラークラス |
| OutOfMemoryError.php | `src/Symfony/Component/ErrorHandler/Error/OutOfMemoryError.php` | ソース | メモリ不足エラークラス |
| ClassNotFoundErrorEnhancer.php | `src/Symfony/Component/ErrorHandler/ErrorEnhancer/ClassNotFoundErrorEnhancer.php` | ソース | クラス未検出エラー強化 |
| ThrowableUtils.php | `src/Symfony/Component/ErrorHandler/ThrowableUtils.php` | ソース | 例外ユーティリティ |
| Resources/ | `src/Symfony/Component/ErrorHandler/Resources/` | テンプレート | エラーページテンプレート |
