# 機能設計書 147-replace

## 概要

本ドキュメントは、Hack Standard Library (HSL) の `Regex\replace` 関数の機能設計を定義する。正規表現パターンにマッチする部分を指定の文字列で置換する関数である。

### 本機能の処理概要

**業務上の目的・背景**：テキスト処理において、パターンにマッチする部分を別の文字列に置換することは基本的な操作である。本機能は、PCREパターンを使用してマッチする全ての部分を置換し、バックリファレンス（\1、\2など）による置換文字列内でのキャプチャグループ参照をサポートする。

**機能の利用シーン**：テキストのフォーマット変換、データクレンジング、文字列の正規化、テンプレート処理など、パターンベースの文字列置換が必要な場面で利用される。

**主要な処理内容**：
1. オフセットの検証と正規化
2. オフセット以降の部分に対してPCRE置換を実行
3. バックリファレンスを処理
4. 置換後の文字列を返却

**関連システム・外部連携**：PCREライブラリに依存する正規表現エンジンを使用。

**権限による制御**：権限による制御は存在しない。

## 関連画面

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

## 機能種別

データ処理 / 文字列操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| $haystack | string | Yes | 置換対象の文字列 | なし |
| $pattern | Pattern<Match> | Yes | PCRE正規表現パターン | PCREパターンとして有効であること |
| $replacement | string | Yes | 置換文字列（バックリファレンス可） | なし |
| $offset | int | No | 置換開始位置（デフォルト: 0） | 文字列長の正負範囲内 |

### 入力データソース

関数呼び出し時の引数

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | string | 置換後の文字列 |

### 出力先

関数の戻り値として返却

## 処理フロー

### 処理シーケンス

```
1. オフセットの検証と正規化
   └─ validate_offset()
2. オフセットが0の場合
   └─ 文字列全体に対して置換を実行
3. オフセットが0でない場合
   └─ 文字列を分割してオフセット以降のみ置換
4. PCREによる置換実行
   └─ _Private\_Regex\replace()
5. エラーチェック
   └─ エラーがあればRegex\Exceptionをスロー
6. 置換後の文字列を返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[オフセット検証]
    B --> C{オフセット = 0?}
    C -->|Yes| D[全体を置換対象とする]
    C -->|No| E[文字列を分割]
    E --> F[オフセット以降を置換対象とする]
    D --> G[PCRE置換を実行]
    F --> G
    G --> H{エラー発生?}
    H -->|Yes| I[Regex\Exception]
    H -->|No| J[置換結果を構築]
    J --> K[結果を返却]
    I --> L[終了]
    K --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-147-1 | 全マッチ置換 | パターンにマッチする全ての部分を置換 | 常時 |
| BR-147-2 | バックリファレンス | \1、\2、${1}などでキャプチャグループを参照可能 | 置換文字列内 |
| BR-147-3 | オフセット以降置換 | オフセット指定時はそれ以降のみ置換 | offsetが指定された場合 |
| BR-147-4 | マッチなし | マッチがない場合は元の文字列をそのまま返却 | 常時 |

### 計算ロジック

- オフセット処理: オフセットが0でない場合、文字列を`Str\slice`で分割して処理
- バックリファレンス: PCREネイティブ関数が処理

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

該当なし（データベース操作を行わない純粋関数）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | InvariantException | オフセットが範囲外 | オフセット値を修正 |
| PREG_* | Regex\Exception | PCREエラー | first_matchと同様 |

### リトライ仕様

該当なし

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

該当なし（データベース操作を行わない）

## パフォーマンス要件

- 時間計算量: O(n * m) - nは文字列長、mはマッチ数
- 空間計算量: O(n) - 置換結果を格納

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

- ReDoS（正規表現DoS）攻撃に注意
- 置換文字列に対する入力検証が必要な場合がある
- `[]`: 純粋コンテキストで実行可能

## 備考

- 関数を使って置換する場合は`Regex\replace_with()`を使用
- バックリファレンス構文: `\1`、`$1`、`${1}`など

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | regex.php | `src/regex/regex.php` | Pattern<Match>型、置換文字列、バックリファレンスを確認 |

**読解のコツ**: 置換文字列内のバックリファレンス（\1、$1、${1}など）はPCREネイティブ関数が処理する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | regex.php | `src/regex/regex.php` | replace関数の定義（99-128行目） |

**主要処理フロー**:
1. **109行目**: `validate_offset`でオフセットを検証
2. **111-117行目**: オフセットが0の場合、全体を置換
3. **120-121行目**: オフセットが0でない場合、文字列を分割
4. **112-113, 122-123行目**: `_Private\_Regex\replace`を呼び出し
5. **114-116, 124-126行目**: エラー時は例外をスロー
6. **117, 127行目**: 結果を返却

#### Step 3: オフセット処理を理解する

**重要なロジック（119-127行目）**:
```hack
$haystack1 = Str\slice($haystack, 0, $offset);
$haystack2 = Str\slice($haystack, $offset);
list($result, $error) =
  _Private\_Regex\replace($haystack2, $pattern, $replacement);
if ($error is nonnull) {
  throw new namespace\Exception($pattern, $error);
}
return $haystack1.($result as nonnull);
```

オフセット以前の部分は変更せず、オフセット以降のみを置換して結合。

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

```
Regex\replace($haystack, $pattern, $replacement, $offset)
    |
    +-- _Private\validate_offset($offset, Str\length($haystack))
    |
    +-- [offset = 0の場合]
    |       |
    |       +-- _Private\_Regex\replace($haystack, $pattern, $replacement)
    |               └─ PCREネイティブ置換
    |
    +-- [offset != 0の場合]
            |
            +-- Str\slice($haystack, 0, $offset)  [前半部分]
            |
            +-- Str\slice($haystack, $offset)  [後半部分]
            |
            +-- _Private\_Regex\replace($haystack2, $pattern, $replacement)
            |       └─ 後半部分のみ置換
            |
            +-- 前半 + 置換結果を結合
```

### データフロー図

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

$haystack ─────┐
               |
$pattern ──────┼──> オフセット処理 ───> PCRE置換
               |          │
$replacement ──┤          v
               |    バックリファレンス処理
$offset ───────┘          │
                          v
                    string ────────> 戻り値
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| regex.php | `src/regex/regex.php` | ソース | replace関数の公開インターフェース |
| private.php | `src/private.php` | ソース | validate_offset関数 |
| RegexTest.php | `tests/regex/RegexTest.php` | テスト | replace関数のテストケース |
