# 機能設計書：プロセス実行

## 1. 機能概要

### 1.1 機能名
プロセス実行（Process）

### 1.2 機能説明
外部コマンドやシェルスクリプトを実行するための機能。Symfony Process コンポーネントをラップし、Fluent API による直感的なプロセス構築、並列実行（Pool）、パイプ処理、テスト用フェイク機能を提供する。

### 1.3 関連画面
- なし（バックエンド専用機能）

## 2. 機能詳細

### 2.1 処理フロー

```
[アプリケーション]
       |
       v
[Process Facade / Factory]
       |
       v
[PendingProcess] ──設定──> command, path, timeout, env
       |
       v
[run() / start()]
       |
       +-- run(): 同期実行（完了まで待機）
       +-- start(): 非同期実行（バックグラウンド）
       |
       v
[ProcessResult / InvokedProcess]
       |
       v
[出力・終了コード取得]
```

### 2.2 クラス構成

| クラス名 | 役割 |
|---------|------|
| Factory | プロセスファクトリー・エントリーポイント |
| PendingProcess | プロセス構築用 Fluent API |
| ProcessResult | 同期実行結果 |
| InvokedProcess | 非同期実行プロセス |
| Pool | 並列プロセス管理 |
| Pipe | パイプ処理管理 |
| FakeProcessResult | テスト用フェイク結果 |
| FakeProcessSequence | テスト用シーケンス |

### 2.3 主要メソッド

#### Factory

| メソッド | 説明 | 引数 | 戻り値 |
|---------|------|------|--------|
| `run()` | コマンドを同期実行 | `$command, $output` | `ProcessResult` |
| `start()` | コマンドを非同期実行 | `$command, $output` | `InvokedProcess` |
| `pool()` | プロセスプール作成 | `$callback` | `Pool` |
| `pipe()` | パイプ処理作成 | `$callback, $output` | `ProcessResult` |
| `concurrently()` | 並列実行して待機 | `$callback, $output` | `ProcessPoolResults` |
| `fake()` | テスト用フェイク登録 | `$callback` | `$this` |
| `result()` | フェイク結果作成 | `$output, $errorOutput, $exitCode` | `FakeProcessResult` |

#### PendingProcess

| メソッド | 説明 | 引数 | 戻り値 |
|---------|------|------|--------|
| `command()` | コマンド設定 | `$command` | `$this` |
| `path()` | 作業ディレクトリ設定 | `$path` | `$this` |
| `timeout()` | タイムアウト設定 | `$timeout` | `$this` |
| `idleTimeout()` | アイドルタイムアウト設定 | `$timeout` | `$this` |
| `forever()` | タイムアウトなし | - | `$this` |
| `env()` | 環境変数設定 | `$environment` | `$this` |
| `input()` | 標準入力設定 | `$input` | `$this` |
| `quietly()` | 出力無効化 | - | `$this` |
| `tty()` | TTYモード有効化 | `$tty` | `$this` |
| `options()` | proc_open オプション | `$options` | `$this` |
| `run()` | 同期実行 | `$command, $output` | `ProcessResult` |
| `start()` | 非同期実行 | `$command, $output` | `InvokedProcess` |

#### ProcessResult

| メソッド | 説明 | 引数 | 戻り値 |
|---------|------|------|--------|
| `output()` | 標準出力取得 | - | `string` |
| `errorOutput()` | 標準エラー出力取得 | - | `string` |
| `exitCode()` | 終了コード取得 | - | `int` |
| `successful()` | 成功判定（終了コード0） | - | `bool` |
| `failed()` | 失敗判定 | - | `bool` |
| `throw()` | 失敗時に例外スロー | - | `$this` |
| `throwIf()` | 条件付き例外スロー | `$condition` | `$this` |

#### InvokedProcess

| メソッド | 説明 | 引数 | 戻り値 |
|---------|------|------|--------|
| `wait()` | 完了まで待機 | `$output` | `ProcessResult` |
| `running()` | 実行中判定 | - | `bool` |
| `output()` | 現在の標準出力 | - | `string` |
| `errorOutput()` | 現在の標準エラー出力 | - | `string` |
| `latestOutput()` | 最新の出力 | - | `string` |
| `latestErrorOutput()` | 最新のエラー出力 | - | `string` |
| `id()` | プロセスID取得 | - | `int\|null` |
| `signal()` | シグナル送信 | `$signal` | `$this` |

### 2.4 ビジネスルール

1. **同期 vs 非同期**
   - `run()`: 完了まで待機、結果を返す
   - `start()`: バックグラウンド実行、後で `wait()` 可能

2. **タイムアウト**
   - デフォルト: 60秒
   - `timeout()` で変更可能
   - `forever()` で無制限

3. **出力処理**
   - リアルタイム出力はコールバックで受信
   - `quietly()` で出力無効化

4. **並列実行**
   - `pool()` で複数プロセスを同時実行
   - `concurrently()` で全完了まで待機

5. **パイプ処理**
   - `pipe()` でプロセス間のパイプ接続
   - 前のプロセスの出力を次の入力に

## 3. 入出力仕様

### 3.1 入力

| 項目 | 型 | 説明 |
|-----|-----|------|
| command | `string\|array` | 実行コマンド |
| path | `string` | 作業ディレクトリ |
| env | `array` | 環境変数 |
| input | `string\|resource` | 標準入力 |
| timeout | `int` | タイムアウト秒数 |

### 3.2 出力

| 項目 | 型 | 説明 |
|-----|-----|------|
| output | `string` | 標準出力 |
| errorOutput | `string` | 標準エラー出力 |
| exitCode | `int` | 終了コード |

## 4. 依存関係

### 4.1 内部依存

| パッケージ | 用途 |
|-----------|------|
| Illuminate\Support\Traits\Macroable | マクロ拡張 |
| Illuminate\Support\Traits\Conditionable | 条件付きチェーン |
| Illuminate\Support\Collection | 結果コレクション |

### 4.2 外部依存

| パッケージ | 用途 |
|-----------|------|
| symfony/process | プロセス実行基盤 |

## 5. エラー処理

| エラー種別 | 発生条件 | 処理内容 |
|-----------|---------|---------|
| ProcessTimedOutException | タイムアウト発生 | 例外スロー |
| ProcessFailedException | throw() でプロセス失敗 | 例外スロー |
| RuntimeException | フェイク未登録で実行 | 例外スロー |

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

### 6.1 推奨読解順序

1. **Factory クラス** (`Process/Factory.php`)
   - **11-15行目**: クラス定義と Macroable トレイト
   - **22-29行目**: プロパティ（recording, recorded, fakeHandlers）
   - **52-60行目**: `result()` - フェイク結果作成
   - **67-70行目**: `describe()` - フェイクライフサイクル
   - **77-81行目**: `sequence()` - フェイクシーケンス
   - **89-112行目**: `fake()` - フェイク登録

2. **テストサポート**
   - **119-122行目**: `isRecording()` - 記録中判定
   - **131-138行目**: `recordIfRecording()` - 条件付き記録
   - **147-152行目**: `record()` - 記録
   - **160-165行目**: `preventStrayProcesses()` - 未フェイク防止
   - **183-195行目**: `assertRan()` - 実行アサーション
   - **204-218行目**: `assertRanTimes()` - 回数アサーション

3. **並列実行**
   - **267-275行目**: `pool()` - プール作成
   - **283-290行目**: `pipe()` - パイプ作成
   - **299-302行目**: `concurrently()` - 並列実行

4. **PendingProcess クラス** (`Process/PendingProcess.php`)
   - **15-17行目**: Conditionable トレイト
   - **31行目**: コマンドプロパティ
   - **38行目**: パスプロパティ
   - **45行目**: タイムアウト（デフォルト60秒）
   - **112-117行目**: `command()` - コマンド設定
   - **125-130行目**: `path()` - 作業ディレクトリ
   - **138-143行目**: `timeout()` - タイムアウト設定
   - **163-168行目**: `forever()` - タイムアウト無効

5. **実行メソッド**
   - **244-263行目**: `run()` - 同期実行
   - **274-289行目**: `start()` - 非同期実行

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

```
Process::run('ls -la')
└── Factory::__call('run', ['ls -la'])
    └── Factory::newPendingProcess()
        └── PendingProcess::run('ls -la')
            ├── フェイク確認
            │   ├── フェイクあり -> FakeProcessResult 返却
            │   └── フェイクなし -> 実行
            └── toSymfonyProcess()
                └── Symfony\Process::run()
                    └── ProcessResult::__construct()

Process::pool(fn ($pool) => [...])
└── Factory::pool()
    └── new Pool($factory, $callback)
        └── Pool::start()
            └── 各 PendingProcess::start()
                └── InvokedProcess[]

Process::pipe(['cmd1', 'cmd2'])
└── Factory::pipe()
    └── new Pipe($factory, $callback)
        └── Pipe::run()
            └── 順次実行、出力を次の入力に
```

### 6.3 データフロー図

```
[コマンド文字列/配列]
    |
    v
[PendingProcess]
    |
    +-- command, path, timeout, env 設定
    |
    v
[run() / start()]
    |
    +-- toSymfonyProcess() -> Symfony Process 作成
    |
    v
[Symfony Process 実行]
    |
    +-- 同期: run() -> 完了まで待機
    +-- 非同期: start() -> バックグラウンド
    |
    v
[ProcessResult / InvokedProcess]
    |
    +-- output(), errorOutput(), exitCode()
    |
    v
[アプリケーションで利用]
```

### 6.4 読解のコツ

- **Symfony ラッパー**: 内部で `Symfony\Component\Process\Process` を使用
- **フェイク機能**: テスト時に実際のプロセス実行をモック
- **__call マジックメソッド**: Factory で未定義メソッドは PendingProcess に委譲
- **Pool/Pipe**: 複数プロセスの管理パターン

### 6.5 関連ファイル一覧

| ファイルパス | 種別 | 役割 |
|------------|------|------|
| `src/Illuminate/Process/Factory.php` | Factory | エントリーポイント |
| `src/Illuminate/Process/PendingProcess.php` | Process | プロセス構築 |
| `src/Illuminate/Process/ProcessResult.php` | Result | 同期実行結果 |
| `src/Illuminate/Process/InvokedProcess.php` | Process | 非同期プロセス |
| `src/Illuminate/Process/Pool.php` | Pool | 並列実行管理 |
| `src/Illuminate/Process/Pipe.php` | Pipe | パイプ処理管理 |
| `src/Illuminate/Process/FakeProcessResult.php` | Fake | テスト用フェイク結果 |
| `src/Illuminate/Process/FakeProcessSequence.php` | Fake | テスト用シーケンス |
| `src/Illuminate/Process/FakeProcessDescription.php` | Fake | テスト用説明 |
| `src/Illuminate/Process/Exceptions/ProcessTimedOutException.php` | Exception | タイムアウト例外 |

## 7. 使用例

```php
use Illuminate\Support\Facades\Process;

// 基本的なコマンド実行
$result = Process::run('ls -la');

if ($result->successful()) {
    echo $result->output();
} else {
    echo $result->errorOutput();
}

// Fluent API
$result = Process::path('/home/user')
    ->timeout(120)
    ->env(['CUSTOM_VAR' => 'value'])
    ->run('npm install');

// 失敗時に例外
Process::run('some-command')->throw();

// 非同期実行
$process = Process::start('long-running-command');

// 他の処理...

$result = $process->wait();

// 並列実行
$pool = Process::pool(function ($pool) {
    $pool->command('npm install');
    $pool->command('composer install');
    $pool->command('yarn build');
});

$results = $pool->start()->wait();

// 簡潔な並列実行
$results = Process::concurrently(function ($pool) {
    $pool->command('task1');
    $pool->command('task2');
});

// パイプ処理
$result = Process::pipe([
    'cat file.txt',
    'grep pattern',
    'wc -l',
]);

// テスト用フェイク
Process::fake([
    'ls *' => Process::result('file1.txt\nfile2.txt'),
    '*' => Process::result('default output'),
]);

// フェイクアサーション
Process::assertRan('ls -la');
Process::assertRanTimes('npm install', 2);
Process::assertNotRan('rm -rf /');
```

## 8. 改版履歴

| バージョン | 日付 | 変更内容 |
|-----------|------|---------|
| 1.0.0 | - | 初版作成 |
