LoginSignup
3
1

More than 5 years have passed since last update.

Silex でお手軽に constructor injection を実現する方法

Last updated at Posted at 2017-06-28

前提

下記のようなコントローラクラス・ルーティングがあって、指定のルートが呼ばれた際(=コントローラクラスがインスタンス化されるとき)にコンストラクタ引数に入っているクラスを勝手にインスタンス化して入れてほしい。

<?php

namespace App\Api\Controller;

use Symfony\Component\HttpFoundation\Request;
use App\Api\Service\PingService;

// controller class
class PingController extends ControllerBase
{
    private $pingService;

    public function __construct(
        PingService $pingService
    ) {
        $this->pingService = $pingService;
    }

    public function get(Request $request)
    {
        $id = $request->get('id', 1);
        return $this->returnJsonResponse([
            'ping' => $this->pingService->getPing(),
            'pong' => $this->pingService->getPong($id),
        ]);
    }
}

// routing
$silexApplication->get('/api/ping', 'App\Api\Controller\PingController::get');

PHP-DI を使う

そこで、PHP-DI という上記のようなことを手っ取り早く実現するためのライブラリがあるので composer require で入れる。

composer require php-di/php-di

コンテナビルダー

下記のような ContainerBuilder というシングルトンなクラスを作る。
パフォーマンスに影響があるようなので、本番向けにはキャッシュの設定などをしたほうが良さそう(公式は APC を使うことを推奨している模様。なお、PHP5.5以降では APCu ですね)。

<?php

namespace App;

use DI\Container;
use Doctrine\Common\Cache\ApcuCache;

class ContainerBuilder
{
    public static function getInstance()
    {
        static $instance;
        if (!($instance instanceof Container)) {
            $builder = new \DI\ContainerBuilder();
            $cache = new ApcuCache();
            $builder->setDefinitionCache($cache);
            $builder->writeProxiesToFile(false);
            $instance = $builder->build();
        }
        return $instance;
    }
}

コントローラリゾルバ

標準で使っているリゾルバ(Silex 2.x では Symfony\Component\HttpKernel\Controller\ControllerResolver)を継承したクラスを作り、instantiateController() をオーバーライドする。
デフォルトではただ単に new $class() として、渡された名前のコントローラクラスをインスタンス化しているだけなので、PHP-DI の機能を使い解決させる。

<?php

namespace App;

class ControllerResolver extends \Symfony\Component\HttpKernel\Controller\ControllerResolver
{
    protected function instantiateController($class)
    {
        $di = ContainerBuilder::getInstance();
        return $di->make($class);
    }
}

Silex\Application に上記のクラスを渡してやる

下記のようにして Application インスタンス(実態は pimple によるDIコンテナ)に上記で作ったコントローラーリゾルバのインスタンスを入れる。

<?php

namespace App;

use Silex\Application;

$app = new Application();
$app['resolver'] = new ControllerResolver();
3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1