# 機能設計書 27-search

## 概要

本ドキュメントは、Hack Standard Library（HSL）の `Str\search` 関数の機能設計書です。文字列内の部分文字列（needle）の最初の出現位置を検索する機能を提供します。

### 本機能の処理概要

`Str\search` 関数は、haystack文字列内でneedle文字列が最初に出現する位置を返す関数です。見つからない場合はnullを返します。PHPの `strpos` 関数に相当しますが、型安全な実装となっています。

**業務上の目的・背景**：文字列処理において、特定のパターンや部分文字列が含まれているかどうか、またその位置を特定することは非常に基本的な操作です。ログ解析、テキスト処理、パーサーの実装、URLの解析など、様々な場面で使用されます。この関数は大文字小文字を区別した検索を行い、オフセット指定による部分検索もサポートします。

**機能の利用シーン**：
- 文字列内の特定パターンの位置特定
- テキスト解析やパーサー実装
- URLやパスからの情報抽出
- ログファイルの検索
- 文字列の分割位置決定

**主要な処理内容**：
1. haystack文字列、needle文字列、オプションのoffsetを受け取る
2. 内部実装関数 `_Str\strpos_l` に処理を委譲
3. needleが見つかった場合はその位置（0始まりのインデックス）を返す
4. needleが見つからない場合はnullを返す

**関連システム・外部連携**：外部システムとの連携はありません。純粋な文字列操作関数です。

**権限による制御**：特に権限による制御はありません。純粋関数（Pure function）として実装されており、副作用はありません。

## 関連画面

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

## 機能種別

計算処理 / 文字列検索

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| $haystack | string | Yes | 検索対象の文字列 | なし（空文字列も許可） |
| $needle | string | Yes | 検索する部分文字列 | なし（空文字列も許可） |
| $offset | int | No | 検索開始位置（デフォルト: 0） | 範囲外の場合例外 |

### 入力データソース

関数呼び出し時の引数として直接渡される文字列データ。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | ?int | needleの開始位置（0始まり）。見つからない場合はnull |

### 出力先

関数の戻り値として呼び出し元に返却。

## 処理フロー

### 処理シーケンス

```
1. 関数呼び出し
   └─ $haystack, $needle, $offset を引数として受け取る
2. オフセット検証
   └─ offsetが範囲外の場合は例外をスロー
3. 内部実装への委譲
   └─ _Str\strpos_l($haystack, $needle, $offset) を呼び出し
4. 結果の変換
   └─ 負の戻り値をnullに変換
5. 結果の返却
   └─ ?int 値を返す
```

### フローチャート

```mermaid
flowchart TD
    A[開始: search呼び出し] --> B{offsetは有効範囲内?}
    B -->|No| C[InvalidArgumentException]
    B -->|Yes| D[strpos_lを呼び出し]
    D --> E{position >= 0?}
    E -->|Yes| F[positionを返す]
    E -->|No| G[nullを返す]
    F --> H[終了]
    G --> H
    C --> H
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 大文字小文字区別 | 検索は大文字小文字を区別する | 常時 |
| BR-02 | 0始まりインデックス | 位置は0始まりで返される | 常時 |
| BR-03 | 負のオフセット | 負のoffsetは末尾からの位置を示す | $offset < 0 |
| BR-04 | 範囲外オフセット | offsetが範囲外の場合は例外 | abs($offset) > length |

### 計算ロジック

- 正のoffset: 先頭からoffsetバイト目から検索開始
- 負のoffset: 末尾からabs(offset)バイト目から検索開始

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

### 操作別データベース影響一覧

本機能はデータベース操作を行いません。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | InvalidArgumentException | offsetが範囲外 | 有効な範囲のoffsetを指定 |

### リトライ仕様

該当なし。

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

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

## パフォーマンス要件

- 計算量: O(n * m) ここで n はhaystack長、m はneedle長
- 純粋関数として実装されており、キャッシュや状態を持たない
- コンテキスト係数 `[]:` が指定されており、副作用なしで実行可能

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

- 純粋関数のため、セキュリティ上の懸念は最小限
- 入力文字列のサニタイズは呼び出し元の責任

## 備考

- 単に含まれているかを確認するだけなら `Str\contains()` を使用すること
- 大文字小文字を無視した検索には `Str\search_ci()` を使用すること
- 最後の出現位置を検索するには `Str\search_last()` を使用すること
- PHPの `strpos` と異なり、見つからない場合は `false` ではなく `null` を返す

---

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

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

### 推奨読解順序

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

まず、外部から呼び出される公開関数のインターフェースを理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | introspect.php | `src/str/introspect.php` | 公開関数 `search` の定義とドキュメントコメント |

**主要処理フロー**:
- **173-189行目**: 関数のドキュメントコメント
- **173-176行目**: 機能概要。needleの最初の位置を返し、見つからない場合はnull
- **177-180行目**: offsetの説明。負の値は末尾からの位置
- **182-184行目**: 関連関数への参照（contains, search_ci, search_last）
- **186行目**: PHPの `strpos` に相当することを明記
- **190-200行目**: 関数シグネチャと実装
- **195行目**: `_Str\strpos_l` への委譲
- **196-199行目**: 負の戻り値をnullに変換

**読解のコツ**: 戻り値が `?int`（null許容int）であることに注目。PHPの `strpos` は `false` を返すが、HSLは `null` を返す型安全な設計。

#### Step 2: ロケール対応版を確認

ロケール対応版の実装を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | introspect_l.php | `src/str/introspect_l.php` | ロケール対応版 `search_l` の実装 |

**主要処理フロー**:
- **188-214行目**: `search_l` の定義
- **209行目**: `_Str\strpos_l` にlocaleを渡して呼び出し

#### Step 3: テストケースを理解する

テストコードから具体的な動作仕様を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | StrIntrospectTest.php | `tests/str/StrIntrospectTest.php` | テストケースから具体的な入出力パターンを理解 |

**主要処理フロー**:
- **257-268行目**: `provideSearch` メソッドでテストデータを定義
- **258行目**: 空のhaystackでは `null`
- **259行目**: `'fooBar'` で `'oB'` を検索すると `2`
- **260行目**: offset 3からの検索では `null`（見つからない）
- **261行目**: 大文字小文字が異なると `null`
- **265-266行目**: 負のoffsetのテスト
- **270-278行目**: テスト実行メソッド
- **329-344行目**: 例外テスト

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

```
Str\search($haystack, $needle, $offset)
    │
    └─ _Str\strpos_l($haystack, $needle, $offset)
           │
           └─ [HHVM内部実装]
                  └─ 文字列検索アルゴリズム
```

### データフロー図

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

$haystack (string) ───┬──▶ search() ───▶ ?int (position or null)
                      │         │
$needle (string) ─────┤         │
                      │         ▼
$offset (int) ────────┘   _Str\strpos_l()
                              │
                              ▼
                        [位置 or -1]
                              │
                              ▼
                        [負なら null に変換]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| introspect.php | `src/str/introspect.php` | ソース | 公開API定義 |
| introspect_l.php | `src/str/introspect_l.php` | ソース | ロケール対応実装 |
| StrIntrospectTest.php | `tests/str/StrIntrospectTest.php` | テスト | ユニットテスト |
