# 画面設計書 7-ソースファイル表示

## 概要

本ドキュメントは、Symfony WebProfilerBundleが提供する「ソースファイル表示」画面の設計書である。本画面はソースファイルの内容を行番号付きでブラウザ上に表示し、指定行へのスクロールやファイルメタデータの表示を行う。

### 本画面の処理概要

本画面は、プロファイラーパネルに表示されるスタックトレースやファイル参照リンクから、該当ソースファイルの内容を行番号付きで表示する画面である。

**業務上の目的・背景**：開発者がデバッグ作業中にスタックトレースやエラー情報から直接ソースコードを確認できるようにすることが目的である。ファイルの全内容を行番号付きで表示し、指定行をハイライトして自動スクロールすることで、問題箇所の特定を効率化する。また、ファイルパス、最終更新日時、ファイルサイズ、行数などのメタデータも提供する。IDE連携のドキュメントリンクも表示される。

**画面へのアクセス方法**：プロファイラーパネル（No.2）内のファイルリンクや、例外詳細画面のスタックトレースに表示されるファイルリンクからアクセスする。URLは`/_profiler/open?file={file}&line={line}`。

**主要な操作・処理内容**：
1. クエリパラメータからfile（相対パス）とline（行番号）を取得
2. baseDirとfileパラメータを結合してフルパスを構築
3. セキュリティチェック（ドットで始まるパスコンポーネントの排除、ファイル読み取り可否の確認）
4. SplFileInfoオブジェクトを生成してファイル情報を取得
5. `file_excerpt` Twigフィルターでファイル内容を行番号付きHTMLに変換
6. 指定行をハイライトし、ページ読み込み完了後にJavaScriptで自動スクロール
7. サイドバーにファイルメタデータとIDE連携リンクを表示

**画面遷移**：
- 遷移元：プロファイラーパネル（No.2）のファイルリンク、例外情報パネル（No.15）のスタックトレースリンク
- 遷移先：特になし（単独の表示画面）

**権限による表示制御**：`baseDir`が設定されていない場合はNotFoundHttpExceptionがスローされる。ディレクトリトラバーサル攻撃を防止するため、ドットで始まるパスコンポーネント（`..`等）を含むファイルパスは拒否される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 63 | WebProfilerBundle | 主機能 | ProfilerController::openActionによるソースファイルの内容表示 |
| 35 | TwigBundle | 補助機能 | Twig Environmentによるopen.html.twigテンプレートのレンダリング |
| 56 | Filesystem | 補助機能 | SplFileInfoによるファイル情報の取得と読み込み可否の判定 |

## 画面種別

詳細（ファイル内容表示）

## URL/ルーティング

| 項目 | 値 |
|------|-----|
| ルート名 | `_profiler_open_file` |
| URLパターン | `/_profiler/open` |
| HTTPメソッド | GET |
| コントローラー | `web_profiler.controller.profiler::openAction` |

## 入出力項目

### 入力（クエリパラメータ）

| 項目名 | 入出力 | 型 | 必須 | 説明 |
|--------|--------|-----|------|------|
| file | 入力 | string | はい | ソースファイルの相対パス（baseDirからの相対） |
| line | 入力 | string | いいえ | ハイライト・スクロール対象の行番号 |

## 表示項目

### メインコンテンツ

| 領域 | 表示内容 |
|------|---------|
| ファイル名ヘッダー | ファイル名と指定行番号（`{file} line {line}`） |
| ソースコンテンツ | 行番号付きのソースコード（file_excerptフィルターで生成されたHTML）。指定行がハイライトされる |
| 読み取り不可メッセージ | "The file is not readable."（ファイルが読み取れない場合） |

### サイドバー

| 項目 | 表示内容 |
|------|---------|
| Filepath | ファイルのフルパス（`file_info.pathname`） |
| Last modified | ファイルの最終更新日時 |
| Size | ファイルサイズ（1KB未満はバイト表示、1KB以上はKB表示）/ 行数 |
| IDEリンク | "Open this file in your IDE?"（Symfony設定ドキュメントへのリンク） |

## イベント仕様

### 1-ファイル表示

1. `baseDir`がnullの場合はNotFoundHttpExceptionをスロー
2. プロファイラー計測を無効化（`$this->profiler?->disable()`）
3. クエリパラメータからfile, lineを取得
4. `baseDir + DIRECTORY_SEPARATOR + file`でフルパスを構築
5. パスにドットで始まるコンポーネントが含まれるか、ファイルが読み取り不可の場合はNotFoundHttpExceptionをスロー
6. SplFileInfoオブジェクトを生成
7. open.html.twigをレンダリング（file_info, file, lineをテンプレート変数として渡す）
8. テンプレート内で`file_excerpt`フィルターによりファイル内容をHTML化
9. 指定行にCSSクラス`selected`を付与
10. ページ読み込み完了後、JavaScriptで`selected`クラスの要素へスムーズスクロール

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| ファイル表示 | なし | なし | ファイルシステムからの読み取りのみ |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|----------|
| file_not_readable | 情報 | "The file is not readable." | file_excerptフィルターがnullを返した場合 |
| base_dir_not_set | エラー | "The base dir should be set." | baseDirが未設定の場合 |
| file_cannot_open | エラー | 'The file "{file}" cannot be opened.' | パスにドットコンポーネントが含まれるか読み取り不可の場合 |

## 例外処理

| 例外クラス | 発生条件 | ユーザーへの影響 |
|-----------|---------|----------------|
| `NotFoundHttpException` | `baseDir`がnullの場合 | HTTP 404エラーページが表示される |
| `NotFoundHttpException` | ファイルパスにドットコンポーネントが含まれる場合 | HTTP 404エラーページが表示される |
| `NotFoundHttpException` | ファイルが読み取り不可の場合 | HTTP 404エラーページが表示される |

## 備考

- `baseDir`はWebProfilerBundleの設定で指定されるプロジェクトルートディレクトリであり、コンストラクタで注入される
- セキュリティのため、`preg_match("'(^|[/\\\\])\.'", $file)`による正規表現でディレクトリトラバーサルを防止している
- `file_excerpt`はTwigの拡張フィルターで、ファイル内容を行番号付きHTMLリストに変換する。第2引数にハイライト行、第3引数に-1を指定するとファイル全体を表示する
- ファイルサイズ表示は1024バイト未満の場合はバイト単位、それ以上はKB単位で表示される
- 自動スクロールは`getBoundingClientRect().y`を使用し、上部20pxのオフセットでスムーズスクロールする
- プロファイラー共通レイアウト（layout.html.twig）ではなく、base.html.twigを直接継承した独自レイアウトを使用

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | profiler.php | `src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.php` | `_profiler_open_file`ルート（36-38行目）の定義を確認 |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ProfilerController.php | `src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php` | `openAction`メソッド（378-400行目）がエントリーポイント。baseDirチェック、セキュリティバリデーション、ファイル読み取りの流れを把握する |

**主要処理フロー**:
1. **380-382行目**: `baseDir`のnullチェック
2. **384行目**: プロファイラー計測の無効化
3. **386-387行目**: クエリパラメータからfile, lineを取得
4. **389行目**: フルパスの構築
5. **391-393行目**: セキュリティチェック（ドットコンポーネント排除、読み取り可否確認）
6. **395-399行目**: SplFileInfoの生成とテンプレートレンダリング

#### Step 3: テンプレートを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | open.html.twig | `src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/open.html.twig` | base.html.twigを直接継承した独自レイアウト。ファイル内容表示（14-27行目）、サイドバーのメタデータ（29-46行目）、自動スクロールJavaScript（52-63行目）を確認 |

**主要処理フロー**:
- **14行目**: `file_info.pathname|file_excerpt(line, -1)`でファイル内容をHTML化
- **18行目**: ファイル名と行番号のヘッダー表示
- **31-44行目**: サイドバーにFilepath, Last modified, Sizeを表示
- **46行目**: IDE設定のドキュメントリンク
- **52-63行目**: 指定行への自動スクロールJavaScript

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

```
ProfilerController::openAction($request)
    |
    +-- [baseDirチェック] (null -> NotFoundHttpException)
    |
    +-- Profiler::disable()
    |
    +-- Request::query->get('file')
    +-- Request::query->get('line')
    |
    +-- [セキュリティバリデーション]
    |       +-- preg_match() [ドットコンポーネント検出]
    |       +-- is_readable() [ファイル読み取り可否]
    |
    +-- new SplFileInfo($filename)
    |
    +-- renderWithCspNonces($request, 'open.html.twig', [...])
            +-- Environment::render()
                    +-- file_excerpt Twigフィルター
```

### データフロー図

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

HTTPリクエスト          --> ProfilerController           --> HTMLレスポンス
(GET /_profiler/open)       ::openAction()                   (open.html.twig)
    |                            |
    +-- file (query)         +-- baseDir + file              ソースコード表示
    +-- line (query)         +-- セキュリティチェック          - 行番号付き
                             +-- SplFileInfo                  - 指定行ハイライト
                             +-- file_excerpt フィルター      - 自動スクロール
                                                              メタデータ表示
                                                              - パス/更新日/サイズ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ProfilerController.php | `src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php` | ソース | openActionメソッドを含むコントローラー |
| open.html.twig | `src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/open.html.twig` | テンプレート | ソースファイル表示のテンプレート |
| open.css.twig | `src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/open.css.twig` | テンプレート | ソースファイル表示用CSS |
| base.html.twig | `src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base.html.twig` | テンプレート | HTML基盤テンプレート（継承元） |
| header.html.twig | `src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/header.html.twig` | テンプレート | ヘッダー部分 |
| profiler.php | `src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.php` | 設定 | ルーティング定義 |
