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


[公式の解説](http://symfony.com/doc/current/book/service_container.html)
[日本語版](http://docs.symfony.gr.jp/symfony2/book/service_container.html)

日本語版記事には，

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

と書かれています（最初は？？？でした）．

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

では，単純にクラスをnewでインスタンス化して使うのと何が違うのかというと，「依存性注入」によって，引数やその他の情報を設定ファイル上から定義できる点です．
依存性注入については，以下の記事がうまくまとまっていると思います．
[猿でも分かる! Dependency Injection: 依存性の注入](http://qiita.com/hshimo/items/1136087e1c6e5c5b0d9f)

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

簡単な例を挙げると，

```php: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 ] # 引数としてサービスコンテナを注入
```

```php: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();
    }
    
}
```

```php:src/ExampleBundle/Services/service1.php
namespace ExampleBundle\Services;

class Service1 {

    public function func() {
        # 処理
    }
    
}
```

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

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

```php: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 ] # 引数としてサービスコンテナを注入
```

```php: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
    }
    
}
```

```php: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クラスを継承したクラス）では，特に設定することなくサービスコンテナを使うことができます．

```php: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>');
    }
    
}