# 機能設計書 145-every_match

## 概要

本ドキュメントは、Hack Standard Library (HSL) の `Regex\every_match` 関数の機能設計を定義する。正規表現パターンで文字列を検索し、全てのマッチをvecとして取得する関数である。

### 本機能の処理概要

**業務上の目的・背景**：テキスト処理において、文字列内のパターンにマッチする全ての部分を取得する必要がある場面は多い。本機能は、PCREパターンを使用して文字列内の全てのマッチを取得し、それぞれのマッチに対して名前付きキャプチャグループを含む型安全なマッチ結果をvecとして返す。

**機能の利用シーン**：ログからの複数イベント抽出、HTMLタグの全抽出、文字列内の全URLの取得など、複数のマッチを一度に取得する場面で利用される。

**主要な処理内容**：
1. オフセット位置から検索を開始
2. マッチが見つかるたびに結果vecに追加
3. 空マッチの場合はオフセットを1進めて無限ループを防止
4. 全てのマッチを含むvecを返却

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

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

## 関連画面

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

## 機能種別

データ処理 / 文字列操作

## 入力仕様

### 入力パラメータ

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

### 入力データソース

関数呼び出し時の引数

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | vec<T> | 全マッチ結果のvec。マッチがない場合は空のvec |

各要素（Match shape）の構造:
- キー0: マッチした文字列全体
- 整数キー: 名前なしキャプチャグループの結果
- 文字列キー: 名前付きキャプチャグループの結果

### 出力先

関数の戻り値として返却

## 処理フロー

### 処理シーケンス

```
1. 文字列長を取得
   └─ Str\length($haystack)
2. 結果vecを空で初期化
   └─ $result = vec[]
3. ループ開始
   └─ while (true)
4. マッチを検索
   └─ _Private\regex_match()
5. マッチなしの場合ループ終了
   └─ break
6. マッチを結果に追加
   └─ $result[] = $match
7. マッチ長を計算
   └─ Str\length(Shapes::at($match, 0))
8. 空マッチの場合オフセットを1進める
   └─ 無限ループ防止
9. マッチありの場合オフセットをマッチ長分進める
10. 結果vecを返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[haystack長を取得]
    B --> C[result = vec[]]
    C --> D[マッチを検索]
    D --> E{マッチあり?}
    E -->|No| F[result を返却]
    E -->|Yes| G[result にマッチを追加]
    G --> H[マッチ長を計算]
    H --> I{マッチ長 = 0?}
    I -->|Yes| J[offset を 1 進める]
    I -->|No| K[offset をマッチ長分進める]
    J --> L{offset > haystack長?}
    L -->|Yes| F
    L -->|No| D
    K --> D
    F --> M[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-145-1 | 全マッチ取得 | 文字列内の全てのマッチを取得する | 常時 |
| BR-145-2 | 空マッチ処理 | 空文字列にマッチした場合、オフセットを1進めて次の検索を行う | マッチ長が0の場合 |
| BR-145-3 | マッチなし | マッチがない場合は空のvecを返す | 常時 |
| BR-145-4 | 重複なし | 同じ位置での重複マッチは発生しない | 常時 |

### 計算ロジック

- マッチ長: `Str\length(Shapes::at($match, 0))`
- 次回検索位置:
  - 空マッチの場合: `$offset++`
  - 通常マッチの場合: `$offset += $match_length`

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | InvariantException | オフセットが範囲外 | オフセット値を修正 |
| PREG_BACKTRACK_LIMIT_ERROR | Regex\Exception | バックトラック制限超過 | パターンを最適化 |
| その他 | Regex\Exception | PCREエラー | first_matchと同様 |

### リトライ仕様

該当なし

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

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

## パフォーマンス要件

- 時間計算量: O(n * m * k) - kはマッチ数
- 空間計算量: O(k * c) - cは各マッチのキャプチャグループ数

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

- ReDoS（正規表現DoS）攻撃に注意
- 大量マッチの場合のメモリ使用量に注意
- `[]`: 純粋コンテキストで実行可能

## 備考

- 最初のマッチのみが必要な場合は`Regex\first_match()`を使用
- マッチの有無のみを判定する場合は`Regex\matches()`を使用

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | regex.php | `src/regex/regex.php` | Pattern<T>型、Match型、戻り値vec<T>を確認 |

**読解のコツ**: 戻り値の`vec<T>`は、各要素がfirst_matchと同じMatch shape構造を持つ。

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

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

**主要処理フロー**:
1. **51行目**: `Str\length($haystack)` で文字列長を取得
2. **52行目**: `$result = vec[]` で結果vecを初期化
3. **53行目**: `while (true)` で無限ループ開始
4. **54行目**: `_Private\regex_match` でマッチを検索
5. **55-57行目**: マッチなしの場合break
6. **58行目**: マッチを結果に追加
7. **59行目**: マッチ長を計算
8. **60-67行目**: 次回検索位置を計算（空マッチ対応）
9. **69行目**: 結果vecを返却

#### Step 3: 空マッチ処理を理解する

**重要なロジック（60-67行目）**:
```hack
if ($match_length === 0) {
  $offset++;
  if ($offset > $haystack_length) {
    break;
  }
} else {
  $offset += $match_length;
}
```

この処理は、空マッチ（例: `/(.?)/`パターン）による無限ループを防ぐために必要。

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

```
Regex\every_match($haystack, $pattern, $offset)
    |
    +-- Str\length($haystack)
    |
    +-- while (true)
            |
            +-- _Private\regex_match($haystack, $pattern, inout $offset)
            |       └─ PCREマッチング
            |
            +-- Str\length(Shapes::at($match, 0))
            |       └─ マッチ長計算
            |
            +-- オフセット更新
```

### データフロー図

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

$haystack ─────┐
               |
$pattern ──────┼──> whileループ ───> 繰り返しマッチング
               |         │
$offset ───────┘         v
                    各マッチ結果
                         │
                         v
                    vec<T> ────────> 戻り値
```

### 関連ファイル一覧

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