Service Container | Symfony7
対象: Symfony 7.4
参考
サービスコンテナ( 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