# 機能設計書 58-Asset

## 概要

本ドキュメントは、Symfony Assetコンポーネントの機能設計を記述する。AssetコンポーネントはCSSやJavaScript、画像ファイル等のWebアセットのURL生成・バージョニング管理機能を提供する。

### 本機能の処理概要

Assetコンポーネントは、WebアプリケーションのアセットファイルのURLを生成する際に、バージョン情報の付与やベースパス/ベースURLの管理を統一的に行う。キャッシュバスティング(キャッシュ無効化)のためのバージョン戦略とCDN対応を提供する。

**業務上の目的・背景**：Webアプリケーションのアセット(CSS、JavaScript、画像等)にはブラウザキャッシュ管理のためにバージョン情報を付与する必要がある。また、CDNやサブドメインを利用したアセット配信では、ベースURLの切り替えが必要となる。Assetコンポーネントは、これらのURL管理を一元化し、テンプレート内での記述を簡潔にする。

**機能の利用シーン**：Twigテンプレートでの `asset()` 関数によるアセットURL生成、CDN切り替え時のURL一括変更、アセットバージョン更新時の自動キャッシュバスティング、HTTPSリクエスト時のアセットURL自動切り替え。

**主要な処理内容**：
1. Packageクラスによるバージョン付きアセットURL生成
2. PathPackageによるベースパス付きアセットURL生成
3. UrlPackageによるベースURL(CDN)付きアセットURL生成
4. Packagesクラスによる名前付きパッケージの管理
5. バージョン戦略の適用(静的バージョン、JSONマニフェスト、空バージョン)
6. HTTPSリクエスト時のSSL対応URLへの自動切り替え

**関連システム・外部連携**：CDNサービス、Webサーバーのアセット配信設定。

**権限による制御**：権限制御は実装されていない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 全画面のアセットURL生成に利用される基盤コンポーネント |

## 機能種別

URL生成 / アセット管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| path | string | Yes | アセットの相対パス(例: 'css/style.css') | 文字列型 |
| packageName | string|null | No | パッケージ名(nullの場合はデフォルトパッケージ) | Packagesに登録済みの名前 |
| basePath | string | Yes(PathPackage) | ベースパス(例: '/assets') | '/'で始まる相対パス |
| baseUrls | string|string[] | Yes(UrlPackage) | ベースURL(例: 'https://cdn.example.com') | 有効なURL |

### 入力データソース

アプリケーション設定(framework.assets)、バージョンマニフェストファイル(JSON形式)。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| url | string | バージョン付きのアセットURL |
| version | string | アセットのバージョン文字列 |

### 出力先

HTMLテンプレート(Twig asset()関数経由)、レスポンスヘッダー。

## 処理フロー

### 処理シーケンス

```
1. Packages::getUrl($path, $packageName)呼び出し
   └─ パッケージ名でPackageInterfaceを解決
2. Package::getUrl($path)呼び出し
   └─ 絶対URLチェック(://や//で始まる場合はそのまま返却)
3. VersionStrategy::applyVersion($path)
   └─ パスにバージョン情報を付与(クエリ文字列またはパス埋め込み)
4. ベースパス/ベースURLの付与
   └─ PathPackage: getBasePath() + versionedPath
   └─ UrlPackage: getBaseUrl() + versionedPath
5. URL返却
```

### フローチャート

```mermaid
flowchart TD
    A[asset関数呼び出し] --> B[Packages::getUrl]
    B --> C[パッケージ解決]
    C --> D{絶対URL?}
    D -->|Yes| E[そのまま返却]
    D -->|No| F[VersionStrategy::applyVersion]
    F --> G{パッケージ種別}
    G -->|Package| H[バージョン付きパス返却]
    G -->|PathPackage| I[basePath + バージョン付きパス]
    G -->|UrlPackage| J{HTTPS?}
    J -->|Yes| K[SSL対応URL選択]
    J -->|No| L[通常URL選択]
    K --> M[baseUrl + バージョン付きパス]
    L --> M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-58-01 | 絶対URLスキップ | '://'または'//'で始まるパスはバージョニング・ベースURL付与をスキップする | Package::isAbsoluteUrl()がtrue |
| BR-58-02 | SSL自動切替 | UrlPackageはHTTPSリクエスト時にSSL対応ベースURLに自動切り替える | Context::isSecure()がtrue |
| BR-58-03 | 一貫性のあるURL分散 | UrlPackageで複数ベースURLがある場合、同一パスは常に同一ベースURLに対応する | crc32ハッシュによる分散 |

### 計算ロジック

UrlPackage::chooseBaseUrl()はパスのcrc32ハッシュをベースURL数で剰余することで、同一パスに対して常に同一のベースURLを返す(UrlPackage.php 108-111行目)。

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | InvalidArgumentException | 存在しないパッケージ名が指定された場合 | Packagesに登録済みの名前を指定 |
| - | LogicException | デフォルトパッケージが未設定でnull名前指定の場合 | setDefaultPackage()でデフォルトを設定 |
| - | LogicException | UrlPackageのベースURLが空の場合 | 1つ以上のベースURLを指定 |
| - | InvalidArgumentException | UrlPackageで不正なURL形式の場合 | 有効なURLスキームを含むURLを指定 |

### リトライ仕様

リトライ機構は不要。

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

トランザクション管理は対象外。

## パフォーマンス要件

- URL生成は軽量な文字列操作のみで高速
- JsonManifestVersionStrategyはマニフェストファイルを一度読み込みキャッシュ

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

- CDN利用時はSRI(Subresource Integrity)の併用を推奨
- HTTPS環境ではUrlPackageのSSL自動切替により混在コンテンツを防止

## 備考

Twig環境では `asset()` 関数としてこのコンポーネントの機能が提供される。Twig Bridgeの `AssetExtension` がPackagesを呼び出す。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | PackageInterface.php | `src/Symfony/Component/Asset/PackageInterface.php` | getUrl()、getVersion()のインターフェース定義 |
| 1-2 | VersionStrategyInterface.php | `src/Symfony/Component/Asset/VersionStrategy/VersionStrategyInterface.php` | バージョン戦略のインターフェース |
| 1-3 | ContextInterface.php | `src/Symfony/Component/Asset/Context/ContextInterface.php` | リクエストコンテキスト(basePath, isSecure) |

#### Step 2: パッケージ実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Package.php | `src/Symfony/Component/Asset/Package.php` | 基本パッケージ、バージョン戦略の適用、絶対URLチェック |
| 2-2 | PathPackage.php | `src/Symfony/Component/Asset/PathPackage.php` | ベースパス付きパッケージ、Context basePath連携 |
| 2-3 | UrlPackage.php | `src/Symfony/Component/Asset/UrlPackage.php` | CDN対応、SSL自動切替、crc32ベースURL分散 |
| 2-4 | Packages.php | `src/Symfony/Component/Asset/Packages.php` | パッケージレジストリ、名前付きパッケージ管理 |

**主要処理フロー**:
- **Package.php 40-47行目**: getUrl() - 絶対URLチェック後にバージョン戦略を適用
- **PathPackage.php 49-59行目**: getUrl() - 親のgetUrl()後にベースパスを付与
- **UrlPackage.php 67-88行目**: getUrl() - SSL判定、バージョン適用、ベースURL選択
- **UrlPackage.php 108-111行目**: chooseBaseUrl() - crc32ハッシュによる分散
- **Packages.php 57-72行目**: getPackage() - 名前によるパッケージ解決

#### Step 3: バージョン戦略を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | StaticVersionStrategy.php | `src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php` | 静的バージョン文字列の付与 |
| 3-2 | JsonManifestVersionStrategy.php | `src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php` | マニフェストJSONからのバージョン解決 |
| 3-3 | EmptyVersionStrategy.php | `src/Symfony/Component/Asset/VersionStrategy/EmptyVersionStrategy.php` | バージョンなし戦略 |

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

```
Packages::getUrl($path, $packageName)
    |
    +-- getPackage($packageName)
    |       +-- $defaultPackage or $packages[$name]
    |
    +-- PackageInterface::getUrl($path)
            |
            +-- [Package] isAbsoluteUrl() -> VersionStrategy::applyVersion()
            +-- [PathPackage] parent::getUrl() -> getBasePath() + ltrim
            +-- [UrlPackage] isAbsoluteUrl() -> sslPackage判定
                    +-- VersionStrategy::applyVersion()
                    +-- getBaseUrl($path) -> chooseBaseUrl(crc32)
```

### データフロー図

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

アセットパス ─────────────> Packages::getUrl()           バージョン付きURL
(例: css/style.css)          |
                             +-- パッケージ解決
                             +-- バージョン付与
                             +-- ベースパス/URL付与
                             |
                             v
                    "/assets/css/style.css?v=1.0"
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| PackageInterface.php | `src/Symfony/Component/Asset/PackageInterface.php` | ソース | パッケージインターフェース |
| Package.php | `src/Symfony/Component/Asset/Package.php` | ソース | 基本パッケージ実装 |
| PathPackage.php | `src/Symfony/Component/Asset/PathPackage.php` | ソース | パスベースパッケージ |
| UrlPackage.php | `src/Symfony/Component/Asset/UrlPackage.php` | ソース | URLベースパッケージ(CDN対応) |
| Packages.php | `src/Symfony/Component/Asset/Packages.php` | ソース | パッケージレジストリ |
| VersionStrategyInterface.php | `src/Symfony/Component/Asset/VersionStrategy/VersionStrategyInterface.php` | ソース | バージョン戦略インターフェース |
| StaticVersionStrategy.php | `src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php` | ソース | 静的バージョン戦略 |
| JsonManifestVersionStrategy.php | `src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php` | ソース | JSONマニフェストバージョン戦略 |
| EmptyVersionStrategy.php | `src/Symfony/Component/Asset/VersionStrategy/EmptyVersionStrategy.php` | ソース | 空バージョン戦略 |
| ContextInterface.php | `src/Symfony/Component/Asset/Context/ContextInterface.php` | ソース | リクエストコンテキストインターフェース |
| NullContext.php | `src/Symfony/Component/Asset/Context/NullContext.php` | ソース | Nullコンテキスト |
