# 機能設計書 16-ExpressionLanguage

## 概要

本ドキュメントは、Symfony ExpressionLanguageコンポーネントの機能設計を記述する。ExpressionLanguageは、式のコンパイルと評価が可能なエンジンを提供し、設定ファイルやアクセス制御等での動的な式評価に使用される。

### 本機能の処理概要

**業務上の目的・背景**：セキュリティのアクセス制御やルーティング条件、サービス定義の条件分岐等において、PHPコードを直接記述せずに簡潔な式で条件を定義する必要がある。ExpressionLanguageは、安全でサンドボックス化された式評価環境を提供し、設定ファイル内での動的な値計算やアクセス制御ルールの表現を可能にする。

**機能の利用シーン**：SecurityBundleのアクセス制御ルール（is_granted等）、ルーティングの条件設定、DI設定の条件分岐、バリデーション制約の条件定義に利用される。

**主要な処理内容**：
1. 式のトークン化（Lexer::tokenize）
2. 式の構文解析（Parser::parse）
3. 式のPHPコードへのコンパイル（Compiler::compile）
4. 式の直接評価（Node::evaluate）
5. 式のキャッシュ管理（PSR-6 CacheItemPool）
6. カスタム関数の登録（register、addFunction）
7. 構文検証（lint）

**関連システム・外部連携**：SecurityBundle（アクセス制御式）、Routing（ルート条件）、DependencyInjection（コンテナ設定式）、Validator（バリデーション式）、Cacheコンポーネント（パース結果キャッシュ）と連携する。

**権限による制御**：ExpressionLanguage自体は権限制御を行わないが、SecurityBundleと連携してアクセス制御式（is_granted('ROLE_ADMIN')等）の評価に使用される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | （直接的な関連画面なし） | - | セキュリティパネル等で式評価結果が間接的に表示される |

## 機能種別

式評価 / コンパイル / 構文解析

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| expression | Expression/string | Yes | 評価またはコンパイルする式 | 構文的に正しいこと（SyntaxError） |
| names | array | No | 式内で使用可能な変数名リスト | 文字列配列 |
| values | array | No | 式評価時の変数値マップ | - |
| flags | int | No | パーサーフラグ（Parser::IGNORE_*） | 整数値 |

### 入力データソース

設定ファイル（YAML、XML）、アノテーション/アトリビュート、アプリケーションコード

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| compiledCode | string | PHPコードにコンパイルされた式 |
| evaluatedResult | mixed | 式の評価結果 |
| parsedExpression | ParsedExpression | パース済み式（ASTノード付き） |

### 出力先

呼び出し元への返却値

## 処理フロー

### 処理シーケンス

```
1. 式の入力受け取り
   └─ Expression|stringとしての式、変数名/値の受け取り
2. キャッシュチェック
   └─ 式+変数名をキーにしてPSR-6キャッシュを検索
3. トークン化（キャッシュミス時）
   └─ Lexer::tokenize() で式をトークン列に分解
4. 構文解析
   └─ Parser::parse() でAST（抽象構文木）を構築
5. キャッシュ保存
   └─ ParsedExpressionをキャッシュに保存
6a. コンパイル（compile呼び出し時）
   └─ Compiler::compile() でASTをPHPコードに変換
6b. 評価（evaluate呼び出し時）
   └─ Node::evaluate() でASTを直接評価
```

### フローチャート

```mermaid
flowchart TD
    A[式入力] --> B[parse呼び出し]
    B --> C{ParsedExpression?}
    C -->|Yes| D[そのまま返却]
    C -->|No| E[キャッシュチェック]
    E --> F{キャッシュヒット?}
    F -->|Yes| G[キャッシュから取得]
    F -->|No| H[Lexer::tokenize]
    H --> I[Parser::parse]
    I --> J[キャッシュ保存]
    J --> G
    G --> K{compile or evaluate?}
    K -->|compile| L[Compiler::compile]
    L --> M[PHPコード文字列]
    K -->|evaluate| N[Node::evaluate]
    N --> O[評価結果]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-16-01 | キャッシュキー | 式文字列+ソート済み変数名をキーとしてキャッシュ | parse実行時 |
| BR-16-02 | 関数登録タイミング | parse/compile/evaluate呼び出し後の関数登録は禁止 | register実行時（parserインスタンス生成後） |
| BR-16-03 | 組み込み関数 | constant、min、max、enumが標準で利用可能 | registerFunctions（コンストラクタ内） |
| BR-16-04 | enum関数 | UnitEnumのみ許容し、非EnumのconstantはTypeErrorをスロー | enum関数のevaluator |

### 計算ロジック

式評価はASTのノードごとに再帰的にevaluate()を呼び出して実行される。コンパイル時は各ノードのcompile()がPHPコード文字列を生成する。

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

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

本コンポーネントはデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | SyntaxError | 式の構文エラー | 式の構文を修正 |
| - | LogicException | パース後の関数登録 | コンストラクタまたはprovider経由で事前登録 |
| - | TypeError | enum関数に非UnitEnumを渡した | UnitEnumの完全修飾名を指定 |

### リトライ仕様

リトライは行わない。

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

トランザクション管理は行わない。

## パフォーマンス要件

- PSR-6キャッシュにより、同一式の再パースを回避
- デフォルトではArrayAdapter（インメモリキャッシュ）を使用
- コンパイル結果をPHPコードとして保存することで、繰り返し評価のオーバーヘッドを削減可能

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

- ExpressionLanguageはサンドボックス化された評価環境であり、任意のPHPコードは実行できない
- 利用可能な関数はregister/addFunctionで明示的に登録されたもののみ
- constant()関数によりPHP定数へのアクセスが可能なため、機密情報を定数に格納しないこと

## 備考

- ExpressionFunctionProviderInterfaceにより、関数セットを再利用可能なプロバイダーとして提供可能
- SerializedParsedExpressionクラスにより、シリアライズされたAST式を効率的に復元可能
- Security CoreのExpressionLanguage拡張でis_granted等のセキュリティ関数が追加される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Expression.php | `src/Symfony/Component/ExpressionLanguage/Expression.php` | 式オブジェクト（__toString対応の文字列ラッパー） |
| 1-2 | Token.php | `src/Symfony/Component/ExpressionLanguage/Token.php` | トークンデータ構造（type、value、cursor） |
| 1-3 | TokenStream.php | `src/Symfony/Component/ExpressionLanguage/TokenStream.php` | トークン列ストリーム |
| 1-4 | ParsedExpression.php | `src/Symfony/Component/ExpressionLanguage/ParsedExpression.php` | パース済み式（AST保持） |
| 1-5 | ExpressionFunction.php | `src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php` | カスタム関数定義（compiler+evaluator） |

**読解のコツ**: ExpressionLanguageは古典的なコンパイラ構成（Lexer→Parser→Compiler）で実装されている。Node/ディレクトリ内にAST(抽象構文木)のノード定義がある。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ExpressionLanguage.php | `src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php` | ファサードクラス。compile、evaluate、parse、lint、registerの各メソッド |

**主要処理フロー**:
1. **37-44行目**: コンストラクタ。キャッシュ初期化、組み込み関数登録、プロバイダー登録
2. **49-52行目**: compile()。parse→getNodes→Compiler::compile→getSourceのチェーン
3. **57-60行目**: evaluate()。parse→getNodes→Node::evaluateのチェーン
4. **67-91行目**: parse()。キャッシュキー構築、キャッシュチェック、Lexer→Parser→キャッシュ保存
5. **120-127行目**: register()。parser生成後の登録を禁止するガード付き
6. **141-160行目**: registerFunctions()。constant、min、max、enumの組み込み関数登録

#### Step 3: パイプライン各段を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Lexer.php | `src/Symfony/Component/ExpressionLanguage/Lexer.php` | 字句解析。文字列→トークン列変換 |
| 3-2 | Parser.php | `src/Symfony/Component/ExpressionLanguage/Parser.php` | 構文解析。トークン列→AST変換 |
| 3-3 | Compiler.php | `src/Symfony/Component/ExpressionLanguage/Compiler.php` | コンパイラ。AST→PHPコード変換 |

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

```
ExpressionLanguage::compile()
    │
    └─ parse()
           ├─ cache->getItem()
           ├─ Lexer::tokenize()
           │      └─ TokenStream生成
           ├─ Parser::parse()
           │      └─ Node階層（AST）生成
           └─ cache->save()
    │
    └─ Compiler::compile()
           └─ Node::compile()（再帰的）

ExpressionLanguage::evaluate()
    │
    └─ parse()（上記と同じ）
    │
    └─ Node::evaluate()（再帰的）
```

### データフロー図

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

式文字列             ───▶ Lexer::tokenize()            ───▶ TokenStream
"a + b > 3"                │
                           ▼
                    Parser::parse()                    ───▶ Node（AST）
                           │
                    ┌──────┴──────┐
                    ▼              ▼
            Compiler::compile()  Node::evaluate()
                    │              │
                    ▼              ▼
            PHPコード文字列      評価結果（mixed）
            "($a + $b > 3)"    true/false
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ExpressionLanguage.php | `src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php` | ソース | ファサードクラス |
| Lexer.php | `src/Symfony/Component/ExpressionLanguage/Lexer.php` | ソース | 字句解析 |
| Parser.php | `src/Symfony/Component/ExpressionLanguage/Parser.php` | ソース | 構文解析 |
| Compiler.php | `src/Symfony/Component/ExpressionLanguage/Compiler.php` | ソース | PHPコードコンパイラ |
| Expression.php | `src/Symfony/Component/ExpressionLanguage/Expression.php` | ソース | 式オブジェクト |
| ParsedExpression.php | `src/Symfony/Component/ExpressionLanguage/ParsedExpression.php` | ソース | パース済み式 |
| ExpressionFunction.php | `src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php` | ソース | カスタム関数定義 |
| ExpressionFunctionProviderInterface.php | `src/Symfony/Component/ExpressionLanguage/ExpressionFunctionProviderInterface.php` | ソース | 関数プロバイダーインターフェース |
| Token.php | `src/Symfony/Component/ExpressionLanguage/Token.php` | ソース | トークンデータ |
| TokenStream.php | `src/Symfony/Component/ExpressionLanguage/TokenStream.php` | ソース | トークンストリーム |
| SyntaxError.php | `src/Symfony/Component/ExpressionLanguage/SyntaxError.php` | ソース | 構文エラー |
| Node/ | `src/Symfony/Component/ExpressionLanguage/Node/` | ソース | ASTノード定義群 |
