対象: Symfony 7.4

参考

Service Container

サービスコンテナ( Service Container )

Symfony の サービスコンテナ( Service Container )は、サービス(クラスのインスタンス)を管理し、依存関係を解決する仕組みです。 登録されたサービスについて、適切に 依存性の注入 を行います。

サービスコンテナ へのサービス登録は、手動(config/services.yaml に定義)でも、自動(Autowiring)でもできます。

依存性の注入( Dependency Injection, DI )

依存性の注入( Dependency Injection, DI )とは、クラスが必要とする依存オブジェクトを、クラスの内部で直接生成するのではなく、外部から注入する設計パターンです。

依存性の注入のおもな方法

  • コンストラクタインジェクション
  • セッターインジェクション
  • プロパティインジェクション

Symfony は、これらの注入を サービスコンテナ が行います。基本はコンストラクタインジェクションを使います。

[!WARNING] サービスコンテナ 自体(ContainerInterface)をクラスに注入して、そこからサービスを取り出す書き方は、依存関係が見えなくなる(サービスロケーターパターン)ため避けてください。

Symfony の 依存性の注入

大きく次の 2 つに分けられます。

  • Autowiring による自動の 依存性の注入
  • config/services.yaml に明示的に定義する手動の 依存性の注入

Autowiring

Symfony は、クラスの 型宣言( type-hints )を使って、設定なしで 依存性の注入 を行えます。この仕組みを Autowiring と呼びます。

自動で解決できるのは、サービスとして登録されたクラス・インターフェースの型宣言です。次のような場合は、明示的な指定が必要です。

  • スカラー値(文字列・数値・環境変数など)を渡したい
  • 同じインターフェースの実装が複数ある
use Symfony\Component\DependencyInjection\Attribute\Autowire;

public function __construct(
    #[Autowire(env: 'ENV_SAMPLE')] private readonly string $envSample,
) {
}

config/services.yaml サンプル

parameters:
    env_sample: '%env(ENV_SAMPLE)%'

services:
    # default configuration for services in *this* file
    _defaults:
        autowire: true      # Automatically injects dependencies in your services.
        autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.

    # ↑↑↑ 著者注釈 デフォルトで Autowiring が有効(型宣言を使用して自動で依存性の注入を解決)

    # makes classes in src/ available to be used as services
    # this creates a service per class whose id is the fully-qualified class name
    App\:
        resource: '../src/'
        exclude:
            - '../src/DependencyInjection/'
            - '../src/Entity/'
            - '../src/Kernel.php'

    # ↑↑↑ 著者注釈 App\ 以下のクラスはデフォルトでサービスコンテナに(サービスとして)登録

    # add more service definitions when explicit configuration is needed
    # please note that last definitions always *replace* previous ones

    # ↓↓↓ 著者注釈 コンストラクタの引数を明示して手動で解決
    # サービス ID をクラス名にすると、上の App\ による自動登録の定義を上書きする
    App\Service\Sample:
        arguments: # constructor injection
            $envSample: '%env_sample%'

    App\Service\MyService:
        arguments: # constructor injection
            $sample: '@App\Service\Sample' # @App\Service\Sample は App\Service\Sample のインスタンス

[!NOTE]

  • ファイル名は services.yaml です(services.yml ではありません)。
  • App\Service\MyService のコンストラクタが Sample $sample と型宣言していれば、arguments の指定がなくても Autowiring で注入されます。上記は明示的に書く場合の例です。
  • 引数名($envSample)は、コンストラクタの引数名と一致させます。

public / private

デフォルトでは、定義されたすべてのサービスはプライベートです。サービスがプライベートである場合、$container->get() を使ってコンテナから直接アクセスすることはできません。ベストプラクティスとして、プライベートサービスのみを作成し、$container->get() を使うのではなく、依存性の注入を使ってサービスを取得すべきです。

もしサービスを遅延ロードしたい場合は、公開サービスを使うのではなく、サービスロケーターを使用することを検討すべきです。

Public Versus Private Services

デバッグコマンド

Autowiring で使用可能なリソース一覧を表示します。

$ bin/console debug:autowiring --all

サービスコンテナ に登録されたサービス一覧を表示します。

$ bin/console debug:container

構文チェック:

$ bin/console lint:yaml config
$ bin/console lint:container