php - 包含已注入(inject)依赖项的对象的对象的 DI 容器

标签 php unit-testing phpunit php-5.3

使用 pimple作为我的 DI 容器,我一直在勇敢地重构小类以依赖 DI 注入(inject),消除我认为可以轻松删除的硬编码依赖项。

我完成这项任务的方法非常简单,但我不知道它是否合适,因为除了上个月在这里学到的知识外,我在 DI 和单元测试方面的经验很少。

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

构造函数根据类型调用适当的创建者方法:

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;
    }
  }
}

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

public function buildDataFactoryContainer($mode=null)

我的想法是,我可以在调用此容器时将 $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');

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

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

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

我意识到我的英语不是很好,希望我已经解释得足够好,让其他 SO'er 能够理解。

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

还有 .. 您如何分离容器配置以创建用于测试目的的对象?

一如既往,欢迎任何评论或相关帖子的链接。

最佳答案

您不应为所有内容创建单独的容器,而应使用单个容器。您可以创建一个容器“扩展”,它基本上只是在您的容器上设置服务。

这是我建议的设置的一个广泛示例。当您只有一个容器时,递归地解决依赖关系是微不足道的。如您所见,security.authentication_provider 依赖于 db,而 db 又依赖于 db.config

由于闭包的惰性,很容易定义服务,然后再定义它们的配置。此外,覆盖它们也很容易,正如您在 TestExtension 中看到的那样。

可以将您的服务定义拆分为多个单独的扩展,以提高它们的可重用性。这几乎就是 Silex microframework 的内容确实(它使用疙瘩)。

我希望这能回答您的问题。

扩展接口(interface)

class ExtensionInterface
{
    function register(Pimple $container);
}

应用扩展

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']);
        });
    }
}

配置扩展

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);
        }
    }
}

config.json

{
    "mailer.config": {
        "username": "something",
        "password": "secret",
        "method":   "smtp"
    },
    "db.config": {
        "username": "root",
        "password": "secret"
    }
}

测试扩展

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

容器工厂

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;
    }
}

申请

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

测试 Bootstrap

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

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

关于php - 包含已注入(inject)依赖项的对象的对象的 DI 容器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6135322/

相关文章:

php - PHPUnit 和数据提供者的问题

php - 关于获取通用模拟对象的简单 PHPUnit 之一

php - 在 AJAX 成功时显示成功消息

unit-testing - 在第三方软件 API (AutoCAD) 上进行单元测试的最佳实践

java - 如何替换对被测试类的私有(private)方法的调用

visual-studio-2010 - 没有 Visual Studio 的 MS 单元测试框架

php - 如何解决 php 7.4 中的 l5-swagger 问题 -"Trying to access array offset on value of type bool"?

php - mysqli 持久连接

php - 使用 html 表单在 ISO-8859-1 环境中发送波兰语字符

PHP 模拟最终类