最小のDIコンテナ in PHP

DIコンテナがなにやら大仰なものとして勘違いされているような気がしたので、機能を極限まで削ぎ落とした最小のDIコンテナを書いた。
これにはAOPは当然ないし、設定ファイルなどもない。

<?php
// DIContainer.php

class DIContainer
{
    protected $componentFactory;
    function __construct(ComponentFactory $c)
    {
        $this->componentFactory = $c;
        $c->accept($this);
    }

    function get($name)
    {
        $name = strtolower($name);
        if (!isset($this->objects[$name])) {
            $this->objects[$name] = $this->componentFactory->get($name);
        }

        return $this->objects[$name];
    }
}

abstract class ComponentFactory
{
    protected $container;
    function get($name)
    {
        return $this->{'build' . $name}();
    }

    function accept(DIContainer $c)
    {
        $this->container = $c;
    }
}

非常に短いですね。

このコンテナをどう使うか

PDOのインスタンスを管理するシンプルな例を用意した。

<?php
include_once dirname(__FILE__) . '/DIContainer.php';

class MyComponentFactory extends ComponentFactory
{
    function buildConfig()
    {
        $config = new stdClass();
        $config->db = 'mysql';
        $config->dbname = 'hoge';
        $config->host = 'localhost';
        $config->user = 'dbusername';
        $config->password = 'dbpassword';
        return $config;
    }

    function buildPDO()
    {
        $config = $this->container->get('config');
        $dsn = "{$config->db}:dbname={$config->dbname};host={$config->host}";
        $pdo = new PDO($dsn, $config->user, $config->password);
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

        return $pdo;
    }
}

$container = new DIContainer(new MyComponentFactory);

// オブジェクトをコンテナから取り出す
$pdo = $container->get('pdo'); 

DIコンテナの利点の一つとして、ロジックを含まないインスタンスの生成を引き受けてくれるという点が挙げられる。
このコンテナだとDIContainer::getメソッドで得られるのはシングルトンなオブジェクトのみだが、もっと機能を持つDIコンテナならgetするたびに新しいインスタンスを得たりというようなこともできるだろう。


DIという単語は日本語に直しても依存性の注入という抽象的な言葉になるので取っ掛かりがなく、わかり辛い。
また、ウェブに転がってるDIに関する文章も一般のデザインパターンに関する文章などと比べて抽象的で難しいものが多い。
が、DIコンテナ自体はそんなに複雑でわかり辛いことをやっているわけではない。
最初の取っ掛かりとしては、DIコンテナインスタンスの管理をやってくれる便利な奴、ぐらいの認識でいいと思う。


DIコンテナでクラス同士を疎結合にする例は、また後のエントリで示す。