Symfony2
PHP5

Symfony2のサービスコンテナについて

More than 3 years have passed since last update.

公式の解説

日本語版

日本語版記事には,


Service Container (サービスコンテナ) (または 依存性注入コンテナ) はサービスのインスタンス化を管理する単純な PHP オブジェクトです。


と書かれています(最初は???でした).

要するに,サービス(PHPのクラスに対応するもの)をSymfony側に登録することにより,後で再利用できるようにするためのものらしいです.

では,単純にクラスをnewでインスタンス化して使うのと何が違うのかというと,「依存性注入」によって,引数やその他の情報を設定ファイル上から定義できる点です.

依存性注入については,以下の記事がうまくまとまっていると思います.

猿でも分かる! Dependency Injection: 依存性の注入

ここで,依存性として注入できるのは,通常の引数だけでなく,設定ファイルのパラメータの値やリクエストオブジェクトやセッションオブジェクト,さらにサービスコンテナ自体など,多岐に渡ります.

簡単な例を挙げると,


src/ExampleBundle/Resources/config/services.yml

# サービスを設定ファイルに登録する

# このyamlファイルは,最終的にapp/config/config.ymlにロードされる必要がある
services:
example.service1:
class: ExampleBundle\Services\Service1
arguments: [ ]
example.logic1:
class: ExampleBundle\Logic\Logic1
arguments: [ @service_container ] # 引数としてサービスコンテナを注入


src/ExampleBundle/Logic/Logic1.php

namespace ExampleBundle\Logic;

use Symfony\Component\DependencyInjection\ContainerInterface as Container;

class Logic1 {

public function __construct(Container $container) { # コンストラクタの引数にサービスコンテナが注入される
$this->container = $container;
}

public function exec() {
$service1 = $this->container->get('example.service1'); # サービスの呼び出し
return $service1->func();
}

}



src/ExampleBundle/Services/service1.php

namespace ExampleBundle\Services;

class Service1 {

public function func() {
# 処理
}

}


このように,サービスコンテナを使うことで,newを使わず,サービスとしてクラスをインスタンス化することができます.

もう少し例を複雑にすると,


src/ExampleBundle/Resources/config/services.yml

# 設定ファイルに記述するパラメータ

parameters:
example.message: hoge

# サービスを設定ファイルに登録する
services:
example.service2:
class: ExampleBundle\Services\Service2
arguments: [ @service_container, 'fuga' ] # 引数としてサービスコンテナと文字列を注入
example.logic2:
class: ExampleBundle\Logic\Logic2
arguments: [ @service_container ] # 引数としてサービスコンテナを注入



src/ExampleBundle/Logic/Logic2.php

namespace ExampleBundle\Logic;

use Symfony\Component\DependencyInjection\ContainerInterface as Container;

class Logic2 {

public function __construct(Container $container) { # コンストラクタの引数にサービスコンテナが注入される
$this->container = $container;
}

public function exec() {
$service1 = $this->container->get('example.service2'); # サービスの呼び出し
$request = $this->container->get('request'); # 現在のリクエストオブジェクトを取得
$currentUrl = $request->getUri(); # 現在のリクエストのURLを取得
$session = $request->getSession(); # 現在のセッションオブジェクトを取得

# ...

return $service1->func(); # hogefuga
}

}



src/ExampleBundle/Services/service2.php

namespace ExampleBundle\Services;

class Service2 {

public function __construct(Container $container, $str) { # コンストラクタの引数にサービスコンテナと文字列が注入される
$this->container = $container;
$this->str = $str;
}

public function func() {
$message = $this->container->getParameter('example.message'); # パラメータの取得
return $message . $this->$str;
}

}


こんな感じで,設定ファイルに記述したパラメータを取得したり,現在のリクエストオブジェクトを取得したりすることができます.

コントローラ以外のクラス内でパラメータやリクエストオブジェクトを取得するためには,実質的にこの方法を使わざるを得ないため(パラメータについては,直接YAMLを読み込むこともできなくはないですが),基本的に自作したクラスはサービスとして登録するようにしておくと良いと思います.

もっと早くこの方法を知りたかったです…

ちなみに,コントローラ(Controllerクラスを継承したクラス)では,特に設定することなくサービスコンテナを使うことができます.


src/ExampleBundle/ExampleController.php

namespace ExampleBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;

class ExampleController extends Controller {

# コンストラクタの定義は不要

public function indexAction($name) {
# ロガーサービスを取得
# コントローラクラスでは,"$this->get('hoge')"が"$this->container->get('hoge')"のショートカットとして使うことができ,
# この行は,"$logger = $this->container->get('logger');"と等価になっている
$logger = $this->get('logger');

$logger->info('hoge'); # ログが出力される

return new Response('<html><body>fuga</body></html>');
}

}