包含已注入依赖项的对象的对象的DI容器

ste*_*lin 5 php phpunit unit-testing php-5.3

使用pimple作为我的DI容器,我一直在勇敢地重构小类以依赖DI注入,消除了我可以看到的易于删除的硬编码依赖.

我完成这项任务的方法非常简单,但是我不知道它是否合适,因为除了我在过去一个月里学到的东西之外,我对DI和单元测试的经验很少.

我创建了一个类,ContainerFactory,它是pimple的子类,并且在该子类中创建了只返回特定对象的容器的方法.

构造函数根据类型调用正确的creator方法:

function __construct($type=null, $mode = null){

 if(isset($type)){  
    switch ($type) {
      case 'DataFactory':
         $this->buildDataFactoryContainer($mode);     
        break;
      case 'DbConnect':
         $this->buildDbConnectContainer($mode);  
        break;
     default:
        return false;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

容器对象创建的方法签名如下:

public function buildDataFactoryContainer($mode=null)
Run Code Online (Sandbox Code Playgroud)

我的想法是,我可以在调用此容器时设置$ mode进行测试,并让它加载测试值而不是实际的运行时设置.我想避免编写单独的容器类进行测试,这是一种我认为没有全部测试相关代码的简单方法.

我可以改为子类化ContainerFactory即:ContainerFactoryTesting extends ContainerFactory并覆盖其中而不是将测试代码与应用程序代码混合,并使用$ mode = null混合方法签名,但这不是本文的重点.继续,为特定对象创建容器,我只需这样做:

 // returns container with DataFactory dependencies, holds $db and $logger objects.
 $dataFactoryContainer = new ContainerFactory('DataFactory');

// returns container with test settings.
$dataFactoryTestContainer = new ContainerFactory('DataFactory','test');

// returns container with DbConnect dependencies, holds dbconfig and $logger objects.
$dbConnectContainer = new ContainerFactory('DbConnect');
Run Code Online (Sandbox Code Playgroud)

我刚遇到一个让我怀疑我正在构建的策略存在缺陷的问题.

综上所述,DataFactory包含保存数据库连接的$ db对象.我现在正在重构这个dbclass以删除它对$ registry对象的依赖关系,但是当我添加需要$ dbConnectContainer的$ db对象时,我将如何创建$ dataFactoryContainer?

例如,在datafactory容器中我添加了一个dbconnect实例,但IT现在需要一个容器传递给它...

我意识到我的英语不是很好,希望我已经解释得很好,以便让一个人了解.

我的问题是双重的,你们如何以简单的方式处理为自身包含依赖项的依赖项创建对象?

并且..为了测试目的,如何分离容器配置以创建对象?

与往常一样,任何评论或相关帖子的链接赞赏.

igo*_*orw 6

您不应该为所有内容创建单独的容器,而是使用单个容器.您可以创建一个容器"Extension",它基本上只是在容器上设置服务.

这是我建议的设置的一个广泛的例子.当你有一个容器时,递归地解析依赖关系是微不足道的.正如你所看到的,security.authentication_provider取决于db,取决于db.config.

由于闭包的懒惰,很容易定义服务,然后在以后定义它们的配置.此外,您可以轻松覆盖它们,正如您可以通过TestExtension看到的那样.

可以将服务定义拆分为多个单独的扩展,以使它们更具可重用性.这几乎就是Silex微框架所做的(它使用了疙瘩).

我希望这回答了你的问题.

ExtensionInterface

class ExtensionInterface
{
    function register(Pimple $container);
}
Run Code Online (Sandbox Code Playgroud)

AppExtension

class AppExtension
{
    public function register(Pimple $container)
    {
        $container['mailer'] = $container->share(function ($container) {
            return new Mailer($container['mailer.config']);
        });

        $container['db'] = $container->share(function ($container) {
            return new DatabaseConnection($container['db.config']);
        });

        $container['security.authentication_provider'] = $container->share(function ($container) {
            return new DatabaseAuthenticationProvider($container['db']);
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

ConfigExtension

class ConfigExtension
{
    private $config;

    public function __construct($configFile)
    {
        $this->config = json_decode(file_get_contents($configFile), true);
    }

    public function register(Pimple $container)
    {
        foreach ($this->config as $name => $value) {
            $container[$name] = $container->protect($value);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

config.json

{
    "mailer.config": {
        "username": "something",
        "password": "secret",
        "method":   "smtp"
    },
    "db.config": {
        "username": "root",
        "password": "secret"
    }
}
Run Code Online (Sandbox Code Playgroud)

TestExtension

class TestExtension
{
    public function register(Pimple $container)
    {
        $container['mailer'] = function () {
            return new MockMailer();
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

ContainerFactory通过

class ContainerFactory
{
    public function create($configDir)
    {
        $container = new Pimple();

        $extension = new AppExtension();
        $extension->register($container);

        $extension = new ConfigExtension($configDir.'/config.json');
        $extension->register($container);

        return $container;
    }
}
Run Code Online (Sandbox Code Playgroud)

应用

$factory = new ContainerFactory();
$container = $factory->create();
Run Code Online (Sandbox Code Playgroud)

测试Bootstrap

$factory = new ContainerFactory();
$container = $factory->create();

$extension = new TestExtension();
$extension->register($container);
Run Code Online (Sandbox Code Playgroud)