# 機能設計書 9-temporary_file

## 概要

本ドキュメントは、Hack Standard Library (HSL) のファイル操作モジュール `HH\Lib\File\temporary_file` 関数の機能設計書である。

### 本機能の処理概要

**業務上の目的・背景**：
アプリケーション開発において、一時的なデータの保存、中間ファイルの生成、テスト用のファイル操作など、一時ファイルを使用するケースは多い。一時ファイルは使用後に削除する必要があるが、例外発生時や異常終了時に削除し忘れるとディスク容量を圧迫する問題がある。`temporary_file`関数は、`IDisposable`パターンを活用して、スコープを抜けると自動的に削除される一時ファイルを作成する。これにより、一時ファイルのライフサイクル管理を簡素化し、リソースリークを防止する。

**機能の利用シーン**：
- ユニットテストでのファイル操作テスト
- 外部コマンド実行時の入出力ファイル
- 大きなデータの一時的なバッファリング
- ファイル変換処理の中間ファイル
- アトミックなファイル更新（一時ファイル作成後にリネーム）

**主要な処理内容**：
1. 一意なファイル名を生成（XXXXXXパターンをランダム文字列に置換）
2. ファイルを排他的に新規作成（O_CREAT | O_EXCL）
3. パーミッション0600で作成（所有者のみ読み書き可能）
4. TemporaryFileインスタンスを返却
5. using文のスコープを抜けると自動的にファイルを削除

**関連システム・外部連携**：
- `OS\mkstemps()`でセキュアな一時ファイル作成
- `_File\open_temporary_fd()`で内部処理
- `CloseableReadWriteHandle`で読み書き操作を提供
- `IDisposable`インターフェースで自動削除を実現

**権限による制御**：ファイルシステムのパーミッションに依存

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 画面なし | - | 本プロジェクトはUIを持たないライブラリのため該当なし |

## 機能種別

ファイルI/O / 一時ファイル管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| prefix | string | No | ファイル名のプレフィックス（デフォルト: 'hack-tmp-'） | 有効なファイル名文字 |
| suffix | string | No | ファイル名のサフィックス（デフォルト: ''） | 有効なファイル名文字 |

### 入力データソース

- プログラムから直接呼び出し（API）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | TemporaryFile | 一時ファイルを管理するDisposableオブジェクト |

### 出力先

- 呼び出し元プログラムへの戻り値
- システム一時ディレクトリ（または指定パス）にファイルを作成

## 処理フロー

### 処理シーケンス

```
1. temporary_file($prefix, $suffix)が呼び出される
   └─ デフォルト値: prefix='hack-tmp-', suffix=''
2. プレフィックスの解釈
   └─ '/'で始まる: 絶対パスとして扱う
   └─ './'または'../'で始まる: 相対パスとして扱う
   └─ それ以外: システム一時ディレクトリを使用
3. open_temporary_fd()でファイル作成
   └─ パターン: {prefix}XXXXXX{suffix}
   └─ OS\mkstemps()で安全にファイルを作成
4. CloseableReadWriteHandleを作成
   └─ FileDescriptorとパスを渡す
5. TemporaryFileインスタンスを作成して返却
   └─ ハンドルをラップ
6. スコープ終了時（__dispose）
   └─ ファイルをクローズ
   └─ ファイルを削除（unlink）
```

### フローチャート

```mermaid
flowchart TD
    A[temporary_file開始] --> B{prefixの形式判定}
    B -->|/で始まる| C[絶対パスとして使用]
    B -->|./または../で始まる| D[相対パスとして使用]
    B -->|それ以外| E[sys_get_temp_dir + prefix]
    C --> F[パターン生成: prefix + XXXXXX + suffix]
    D --> F
    E --> F
    F --> G[OS::mkstemps でファイル作成]
    G --> H{作成成功?}
    H -->|No| I[ErrnoException]
    H -->|Yes| J[FileDescriptor と path 取得]
    J --> K[CloseableReadWriteHandle作成]
    K --> L[TemporaryFile作成]
    L --> M[TemporaryFile返却]
    M --> N{using スコープ終了?}
    N -->|Yes| O[__dispose: close + unlink]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 排他的作成 | O_CREAT \| O_EXCLで新規ファイルのみ作成 | 常時 |
| BR-02 | セキュアパーミッション | モード0600で作成（所有者のみ読み書き） | 常時 |
| BR-03 | 自動削除 | usingスコープ終了時に自動的にファイルを削除 | IDisposable使用時 |
| BR-04 | 親ディレクトリ必須 | プレフィックスで指定されたパスの親ディレクトリが存在する必要がある | 常時 |
| BR-05 | 一意ファイル名 | XXXXXXパターンがランダム文字列に置換され一意のファイル名を生成 | 常時 |

### プレフィックスの解釈

| プレフィックス例 | 解釈 | 結果パス |
|-----------------|------|---------|
| `'hack-tmp-'` | システム一時ディレクトリ | `/tmp/hack-tmp-abc123` |
| `'/custom/path/file-'` | 絶対パス | `/custom/path/file-abc123` |
| `'./local-'` | カレントディレクトリからの相対パス | `./local-abc123` |
| `'../parent-'` | 親ディレクトリからの相対パス | `../parent-abc123` |

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

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ENOENT | OS\ErrnoException | 親ディレクトリが存在しない | ディレクトリを作成する |
| EACCES | OS\ErrnoException | 書き込み権限がない | パーミッションを確認 |
| ENOSPC | OS\ErrnoException | ディスク容量不足 | 空き容量を確保 |
| EROFS | OS\ErrnoException | 読み取り専用ファイルシステム | 書き込み可能なパスを使用 |

### リトライ仕様

本機能自体にリトライ機構は組み込まれていない。ファイル名の衝突は内部で自動的に回避される。

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

該当なし（__disposeでの削除は最善努力、EBADFエラーは無視）

## パフォーマンス要件

- ファイル作成はOSのシステムコールに依存
- 一時ディレクトリは通常RAMディスクまたは高速ストレージ上にある
- 大量の一時ファイルを作成する場合はinode枯渇に注意

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

- パーミッション0600により他ユーザーからのアクセスを防止
- O_EXCLフラグにより既存ファイルへのシンボリックリンク攻撃を防止
- mkstemps()によりレースコンディションを回避
- センシティブなデータを一時ファイルに書き込む場合でも、自動削除により情報漏洩リスクを低減

## 備考

- `leaky_temporary_file`は自動削除しないバージョン（非推奨パターン）
- getHandle()でCloseableReadWriteHandleを取得可能
- using文を使用しない場合、手動でclose()とunlink()が必要
- __dispose()でのEBADFエラーは無視される（既にクローズ済みの場合）

---

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

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

### 推奨読解順序

#### Step 1: 関数シグネチャを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | temporary_file.php | `src/file/temporary_file.php` | temporary_file関数（36-43行目） |

**読解のコツ**: `<<__ReturnDisposable>>`属性はIDisposableを返す関数に必要。

**主要処理フロー**:
- **36行目**: `<<__ReturnDisposable>>` - Disposable返却を示す属性
- **38行目**: `string $prefix = 'hack-tmp-'` - デフォルトプレフィックス
- **39行目**: `string $suffix = ''` - デフォルトサフィックス
- **41行目**: `_File\open_temporary_fd($prefix, $suffix)` - ファイル作成
- **42行目**: `new TemporaryFile(new _File\CloseableReadWriteHandle($fd, $path))` - ラップ

#### Step 2: 内部ファイル作成処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | open_temporary_fd.php | `src/file/_Private/open_temporary_fd.php` | open_temporary_fd関数（15-30行目） |

**主要処理フロー**:
- **20-24行目**: プレフィックスの形式判定（/, ./, ../）
- **26行目**: システム一時ディレクトリを使用する場合の処理
- **28行目**: パターン生成: `{prefix}XXXXXX{suffix}`
- **29行目**: `OS\mkstemps($pattern, Str\length($suffix))` - ファイル作成

#### Step 3: TemporaryFileクラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TemporaryFile.php | `src/file/TemporaryFile.php` | TemporaryFileクラス（16-33行目） |

**主要処理フロー**:
- **16行目**: `implements \IDisposable` - Disposableパターン
- **17行目**: コンストラクタでCloseableReadWriteHandleを受け取る
- **19-21行目**: `getHandle()` - ハンドルへのアクセサ
- **22-32行目**: `__dispose()` - クローズと削除処理

#### Step 4: 削除処理の詳細を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | TemporaryFile.php | `src/file/TemporaryFile.php` | __dispose()メソッド（22-32行目） |

**主要処理フロー**:
- **24行目**: `$f->close()` - ファイルをクローズ
- **25-29行目**: EBADFエラー（既にクローズ済み）は無視
- **31行目**: `\unlink($f->getPath())` - ファイルを削除

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

```
File\temporary_file($prefix, $suffix)
    |
    +-- _File\open_temporary_fd($prefix, $suffix)
    |       |
    |       +-- [プレフィックス解釈]
    |       |       - /で始まる → 絶対パス
    |       |       - ./, ../で始まる → 相対パス
    |       |       - それ以外 → sys_get_temp_dir() + prefix
    |       |
    |       +-- パターン生成: {prefix}XXXXXX{suffix}
    |       |
    |       +-- OS\mkstemps($pattern, $suffix_length)
    |               |
    |               +-- (FileDescriptor, path) を返却
    |
    +-- new _File\CloseableReadWriteHandle($fd, $path)
    |
    +-- new TemporaryFile($handle)
            |
            +-- implements IDisposable
            +-- getHandle() → CloseableReadWriteHandle
            +-- __dispose()
                    |
                    +-- $handle->close()
                    +-- \unlink($path)
```

### データフロー図

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

string $prefix
string $suffix
    |
    v
┌─────────────────────────────────────────────────────────────────────┐
│ open_temporary_fd()                                                 │
│   - プレフィックス解釈                                               │
│   - パターン生成: {prefix}XXXXXX{suffix}                            │
│   - OS\mkstemps() でファイル作成                                    │
│   - 戻り値: (FileDescriptor, path)                                  │
└─────────────────────────────────────────────────────────────────────┘
    |
    v
┌─────────────────────────────────────────────────────────────────────┐
│ TemporaryFile                                                       │
│   - CloseableReadWriteHandle をラップ                               │
│   - getHandle() で読み書き操作                                      │
│   - IDisposable で自動削除                                          │
└─────────────────────────────────────────────────────────────────────┘
    |
    v
using ($tf = File\temporary_file()) {
    $handle = $tf->getHandle();
    // 読み書き操作
}
// ← 自動的にclose() + unlink()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| temporary_file.php | `src/file/temporary_file.php` | ソース | temporary_file関数の定義 |
| TemporaryFile.php | `src/file/TemporaryFile.php` | ソース | TemporaryFileクラスの定義 |
| open_temporary_fd.php | `src/file/_Private/open_temporary_fd.php` | ソース | 内部ファイル作成処理 |
| CloseableReadWriteHandle.php | `src/file/_Private/CloseableReadWriteHandle.php` | ソース | ハンドル実装 |
| leaky_temporary_file.php | `src/file/leaky_temporary_file.php` | ソース | 自動削除しないバージョン |
| FileTest.php | `tests/file/FileTest.php` | テスト | 一時ファイルのテスト |
