# 機能設計書 144-first_match

## 概要

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

### 本機能の処理概要

**業務上の目的・背景**：テキスト処理において、正規表現による文字列検索は最も基本的かつ強力な操作の一つである。本機能は、PCRE（Perl Compatible Regular Expressions）パターンを使用して文字列内の最初のマッチを取得し、名前付きキャプチャグループを含む型安全なマッチ結果を返す。

**機能の利用シーン**：ログ解析、フォームバリデーション、テキストパースイング、データ抽出など、文字列からパターンにマッチする部分を取得する場面で利用される。

**主要な処理内容**：
1. オフセット位置を検証
2. 内部のregex_match関数を呼び出し
3. マッチ結果（Match shape）またはnullを返却

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

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

## 関連画面

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

## 機能種別

データ処理 / 文字列操作

## 入力仕様

### 入力パラメータ

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

### 入力データソース

関数呼び出し時の引数

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | ?T | マッチ結果（Match shape）。マッチしない場合はnull |

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

### 出力先

関数の戻り値として返却

## 処理フロー

### 処理シーケンス

```
1. オフセットの検証
   └─ _Private\regex_matchに委譲
2. PCREマッチング実行
   └─ _Private\_Regex\matchを呼び出し
3. エラーチェック
   └─ エラーがあればRegex\Exceptionをスロー
4. マッチ結果を返却
   └─ マッチあり: T型のshape、なし: null
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[オフセット検証]
    B --> C{オフセット有効?}
    C -->|No| D[InvariantException]
    C -->|Yes| E[PCREマッチング実行]
    E --> F{エラー発生?}
    F -->|Yes| G[Regex\Exception]
    F -->|No| H{マッチあり?}
    H -->|Yes| I[Match shape を返却]
    H -->|No| J[null を返却]
    D --> K[終了]
    G --> K
    I --> K
    J --> K
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-144-1 | オフセット検証 | オフセットは文字列長の正負範囲内であること | 常時 |
| BR-144-2 | 負のオフセット | 負のオフセットは末尾からの位置として解釈 | offsetが負の場合 |
| BR-144-3 | 空マッチ | 空文字列にマッチするパターンも有効 | 常時 |
| BR-144-4 | キャプチャグループ | 名前付き・名前なしキャプチャグループの結果を含む | パターンにキャプチャグループがある場合 |

### 計算ロジック

- オフセット正規化: 負の場合は`$offset += Str\length($haystack)`

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | InvariantException | オフセットが範囲外 | オフセット値を修正 |
| PREG_INTERNAL_ERROR | Regex\Exception | PCREの内部エラー | パターンを確認 |
| PREG_BACKTRACK_LIMIT_ERROR | Regex\Exception | バックトラック制限超過 | パターンを最適化 |
| PREG_RECURSION_LIMIT_ERROR | Regex\Exception | 再帰制限超過 | パターンを簡素化 |
| PREG_BAD_UTF8_ERROR | Regex\Exception | 無効なUTF-8文字列 | 入力文字列を確認 |
| PREG_BAD_UTF8_OFFSET_ERROR | Regex\Exception | 無効なUTF-8オフセット | オフセットを確認 |
| PREG_BAD_REGEX_ERROR | Regex\Exception | パターンのコンパイル失敗 | パターン構文を確認 |
| PREG_JIT_STACKLIMIT_ERROR | Regex\Exception | JITスタック制限超過 | パターンを簡素化 |

### リトライ仕様

該当なし

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

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

## パフォーマンス要件

- 時間計算量: O(n * m) - 最悪ケース（nは文字列長、mはパターン複雑度）
- 空間計算量: O(k) - kはキャプチャグループ数

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

- ReDoS（正規表現DoS）攻撃に注意：バックトラックが多発するパターンは避ける
- `[]`: 純粋コンテキストで実行可能

## 備考

- PCREパターン構文: https://www.pcre.org/original/doc/html/pcresyntax.html
- 全てのマッチを取得する場合は`Regex\every_match()`を使用
- マッチの有無のみを判定する場合は`Regex\matches()`を使用

---

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

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

### 推奨読解順序

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

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

**読解のコツ**:
- `Pattern<T>`は正規表現パターンを表す特殊な文字列型
- `Match`はshape型で、キー0にマッチ全体、数値キーにキャプチャグループ、文字列キーに名前付きキャプチャグループが格納される
- `re"/pattern/"`リテラル構文でPattern型を生成

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

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

**主要処理フロー**:
1. **29-33行目**: 関数シグネチャ - ジェネリック型T as Matchでマッチ結果の型を指定
2. **34行目**: `_Private\regex_match`を呼び出し、実際のマッチ処理を委譲

#### Step 3: 内部実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | private.php | `src/regex/private.php` | regex_match関数の実装（27-38行目） |
| 3-2 | private.php | `src/private.php` | validate_offset関数の実装（17-29行目） |

**主要処理フロー**:
- **32行目**: `validate_offset`でオフセットを検証
- **33行目**: `_Regex\match`（ネイティブヘルパー）を呼び出し
- **34-36行目**: エラーがあれば`Regex\Exception`をスロー
- **37行目**: マッチ結果を返却

#### Step 4: 例外クラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | exception.php | `src/regex/exception.php` | Regex\Exceptionクラスの定義（15-37行目） |

**主要処理フロー**:
- **17-25行目**: PCREエラーコードとエラーメッセージのマッピング
- **26-35行目**: パターンとエラーコードからエラーメッセージを生成

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

```
Regex\first_match($haystack, $pattern, $offset)
    |
    +-- _Private\regex_match($haystack, $pattern, inout $offset)
            |
            +-- _Private\validate_offset($offset, Str\length($haystack))
            |
            +-- _Private\_Regex\match($haystack, $pattern, inout $offset)
            |       └─ PCREネイティブ関数
            |
            +-- [エラー時] throw Regex\Exception($pattern, $error)
```

### データフロー図

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

$haystack ─────┐
               |
$pattern ──────┼──> _Private\regex_match ───> PCREマッチング
               |           │
$offset ───────┘           v
                    マッチ結果/null ────────> 戻り値 (?T)
                           │
                    [エラー時]
                           v
                    Regex\Exception
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| regex.php | `src/regex/regex.php` | ソース | first_match関数の公開インターフェース |
| private.php | `src/regex/private.php` | ソース | regex_match内部実装 |
| private.php | `src/private.php` | ソース | validate_offset関数 |
| exception.php | `src/regex/exception.php` | ソース | Regex\Exception例外クラス |
| RegexTest.php | `tests/regex/RegexTest.php` | テスト | first_match関数のテストケース |
