# 機能設計書 42-replace_every_nonrecursive

## 概要

本ドキュメントは、Hack Standard Library (HSL) の `Str\replace_every_nonrecursive` 関数の機能設計を記載する。この関数は文字列内の複数の検索パターンを対応する置換文字列で一括置換するが、置換後の新しい値は再度検索されない（非再帰的）という特徴を持つ。

### 本機能の処理概要

**業務上の目的・背景**：文字列置換処理において、置換結果が更に置換対象となることを避けたい場面がある。例えば、HTMLエンティティのエンコード、双方向の文字置換（例：'a'を'b'に、'b'を'a'に同時に置換）などで、再帰的な置換は意図しない結果を引き起こす。本機能はこのような要件を安全に実現する。

**機能の利用シーン**：
- HTMLエンティティのエンコード（`<`を`&lt;`に変換後、`&`が再度置換されないようにする）
- 文字の双方向スワップ（'H'を'h'に、'h'を'H'に同時に置換）
- テンプレート変数の置換で、置換値に別の変数パターンが含まれる場合
- プレースホルダーの一括展開

**主要な処理内容**：
1. 入力文字列（haystack）と検索/置換ペア（replacements）を受け取る
2. haystack内で各検索パターンを順次検索
3. マッチした箇所を対応する値で置換（一度置換された部分は再検索しない）
4. 複数のオーバーラップするマッチがある場合、haystack内で先に出現するものが優先
5. 同じ位置で複数のマッチがある場合、長い方のマッチが優先

**関連システム・外部連携**：HSL内部の `_Private\_Str\replace_every_nonrecursive_l` 関数に処理を委譲。

**権限による制御**：本機能は純粋関数であり、権限による制御は行わない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | 画面なし | - | 本プロジェクト（Hack Standard Library）はUIを持たないライブラリプロジェクトのため、画面機能マッピングは該当しません。 |

## 機能種別

文字列変換処理（Transform）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| $haystack | string | Yes | 検索/置換対象の文字列 | なし |
| $replacements | KeyedContainer<string, string> | Yes | 検索文字列をキー、置換文字列を値とするコンテナ | 空文字列のキーは不可 |

### 入力データソース

関数呼び出し時の引数として直接渡される。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| (戻り値) | string | 置換処理が適用された結果文字列 |

### 出力先

関数の戻り値として返却される。

## 処理フロー

### 処理シーケンス

```
1. 引数の受け取り
   └─ $haystack（対象文字列）と$replacements（置換ペア）を受け取る
2. バリデーション
   └─ 空文字列のキーがある場合はInvalidArgumentExceptionをスロー
3. 内部関数への委譲
   └─ KeyedContainerをdictに変換
4. _Str\replace_every_nonrecursive_lの呼び出し
   └─ 非再帰的な置換を実行
5. 結果の返却
   └─ 置換後の文字列を返す
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[引数を受け取る]
    B --> C{空のキーが存在?}
    C -->|Yes| D[InvalidArgumentExceptionをスロー]
    C -->|No| E[replacementsをdictに変換]
    E --> F[_Str\replace_every_nonrecursive_l呼び出し]
    F --> G[置換後の文字列を返却]
    G --> H[終了]
    D --> I[異常終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-42-01 | 非再帰的置換 | 置換後の新しい値は再度検索対象とならない | 常時 |
| BR-42-02 | 位置優先 | haystack内で先に出現するマッチが優先される | オーバーラップ発生時 |
| BR-42-03 | 長さ優先 | 同じ位置で複数のマッチがある場合、長い方が優先される | 同位置マッチ発生時 |
| BR-42-04 | 順序非依存 | replacementsの順序は結果に影響しない | 常時 |
| BR-42-05 | 空キー禁止 | 検索文字列として空文字列は使用不可 | 常時 |

### 計算ロジック

特になし（文字列のパターンマッチングと置換のみ）

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

本機能はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | InvalidArgumentException | replacementsのキーに空文字列が含まれる場合 | 空文字列以外のキーを使用する |

### リトライ仕様

該当なし（同期的な純粋関数）

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

該当なし（データベース操作なし）

## パフォーマンス要件

- 時間計算量: O(a + length * b)、a = 全キー長の合計、length = haystack長、b = 異なるキー長の数
- 空間計算量: O(n)、結果文字列のサイズ

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

- 入力文字列のサニタイズは呼び出し側の責任
- 本関数自体は副作用を持たない純粋関数

## 備考

- 大文字小文字を区別しない場合は `Str\replace_every_nonrecursive_ci` を使用すること
- 再帰的な置換が必要な場合は `Str\replace_every` を使用すること

---

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

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

### 推奨読解順序

#### Step 1: 公開API関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | transform.php | `src/str/transform.php` | 公開関数replace_every_nonrecursiveの定義とドキュメンテーション |

**主要処理フロー**:
1. **265-270行目**: replace_every_nonrecursive関数の定義。KeyedContainerを受け取り、dictに変換して内部関数に委譲

**読解のコツ**: この関数は `[]:` 属性を持ち、純粋関数（副作用なし）であることを示している。

#### Step 2: ドキュメンテーションコメントを確認する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | transform.php | `src/str/transform.php` | 251-264行目のコメントで動作仕様を確認 |

**主要処理フロー**:
- **257-258行目**: 長いマッチが優先される仕様の説明
- **259行目**: replacementsの順序は結果に影響しないことの説明

#### Step 3: テストケースで挙動を確認する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | StrTransformTest.php | `tests/str/StrTransformTest.php` | replace_every_nonrecursiveのテストケースを確認 |

**主要処理フロー**:
- **480-562行目**: provideReplaceEveryNonrecursiveで様々な入力パターンと期待結果を確認
- **564-572行目**: 空キーでの例外発生テスト

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

```
Str\replace_every_nonrecursive($haystack, $replacements)
    │
    └─ _Str\replace_every_nonrecursive_l($haystack, dict($replacements))
           │
           └─ [HHVM内部実装]
```

### データフロー図

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

$haystack (string) ─────┐
                        ├──▶ バリデーション ──▶ dict変換 ──▶ _Str\replace_every_nonrecursive_l ──▶ 置換後文字列
$replacements (dict) ───┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| transform.php | `src/str/transform.php` | ソース | 公開API関数の定義 |
| transform_l.php | `src/str/transform_l.php` | ソース | ロケール対応版の定義 |
| StrTransformTest.php | `tests/str/StrTransformTest.php` | テスト | 単体テスト |
