【问题标题】:Load custom configuration in a console command using dependency-injection使用依赖注入在控制台命令中加载自定义配置
【发布时间】:2020-08-11 09:00:30
【问题描述】:

我已经开始使用 Symfony 的控制台组件来构建各种 cli 工具。

我目前正在拼凑这样一个控制台应用程序,它需要各种配置,其中一些在命令之间共享,其他配置是命令独有的。

一开始我使用了一个辅助类,通过一个静态函数调用来加载一个常规的配置数组。

昨天我重构了这个,现在在配置组件中加载配置,以及用于验证的 treeBuilder 机制。这一切都在主控制台脚本中完成,而不是在“命令”类中。

$app = new Application('Console deployment Application', '0.0.1');


/**
 *  Load configuration
 */
$configDirectories = array(__DIR__.'/config');
$locator = new FileLocator($configDirectories);

$loader = new YamlConfigLoader($locator);

$configValues = $loader->load(file_get_contents($locator->locate("config.yml")));
    
// process configuration
$processor = new Processor();

$configuration = new Configuration();

try {
  $processedConfiguration = $processor->processConfiguration(
    $configuration,
    $configValues
  );

  // configuration validated
  var_dump($processedConfiguration);

} catch (Exception $e) {
  // validation error
  echo $e->getMessage() . PHP_EOL;
}

/**
 *  Load commands
 */
foreach(glob(__DIR__ . '/src/Command/*Command.php') as $FileName) {
    $className = "Command\\" . rtrim(basename($FileName), ".php");
    $app->addCommands(array(
        new $className,
    ));
}

$app->run();

目前,设置配置的唯一方法是将加载配置的代码设置在单独的类中,并在每个方法的 configure() 方法中调用该类。

也许我错过了一种更“symfonyish”的方式,我也想避免将整个框架放在代码库中,这意味着它是一个轻量级的控制台应用程序。

有没有办法使用 DI 或其他我不知道的方法将处理后的配置传递给正在调用的命令?

【问题讨论】:

  • 您需要对某些/所有命令进行不同的配置吗?除了某些/所有命令中的配置之外,您还需要其他依赖项吗?如果你回答 2x 否,那为什么不new $className($configuration)
  • @FaKeller - 我想我可以设法让所有命令使用相同的配置文件,并且不需要其他依赖项。我确实在这里找到了一个 DI 组件:github.com/luismulinari/consolefull 但这增加了不必要的依赖关系,我试图保持这一点。如果我理解正确,我可以做类似的事情: $application = new Application( 'Application name', 'Version', new ContainerConfig(); ); ?我刚刚进入 symfony 组件,如果您可以回复并详细说明一下,您的解决方案似乎符合我的目的。

标签: symfony symfony-2.1


【解决方案1】:

手动注入

如果您想保持轻松,并且所有命令只有一个(相同的)配置对象,那么您甚至不需要 DI 容器。只需像这样创建命令:

...
$app->addCommands(array(
    new $className($configuration),
));

尽管您必须注意权衡取舍,例如将来您将不得不付出更多努力来扩展它或适应不断变化的需求。

简单的 DI 容器

您当然可以使用 DI 容器,有一个名为 Twittee 的非常轻量级的容器,它少于 140 个字符(因此适合 tweet)。您可以简单地复制并粘贴它并且不添加任何依赖项。在您的情况下,这可能最终看起来类似于:

$c = new Container();
$c->configA = function ($c) {
  return new ConfigA();
};
$c->commandA = function($c) {
  return new CommandA($c->configA());
}
// ...

然后您需要为所有命令和配置设置它,然后只需为每个命令设置:

$app->addCommand($c->commandA());

接口注入

您可以使用接口和 setter 注入来推出自己的简单注入机制。对于要注入的每个依赖项,您都需要定义一个接口:

interface ConfigAAwareInterface {
    public function setConfigA(ConfigA $config);
}
interface ConfigBAwareInterface {
    public function setConfigA(ConfigA $config);
}

任何需要依赖的类都可以简单地实现接口。由于您将主要重复设置器,因此请使用 trait:

trait ConfigAAwareTrait {
    private $config;
    public function setConfigA(ConfigA $config) { $this->config = $config; }
    public function getConfigA() { return $this->config }
}

class MyCommand extends Command implements ConfigAAwareInterface {
    use ConfigAAwareTrait;

    public function execute($in, $out) {
        // access config
        $this->getConfigA();
    }
}

现在剩下的就是实际实例化命令并注入依赖项。您可以使用以下简单的“注入器类”:

class Injector {
    private $injectors = array();
    public function addInjector(callable $injector) {
      $this->injectors[] = $injector;
    }
    public function inject($object) {
        // here we'll just call the injector callables
        foreach ($this->injectors as $inject) {
            $inject($object);
        }
        return $object;
    }
}

$injector = new Injector();

$configA = new ConfigA();
$injector->addInjector(function($object) use ($configA) {
    if ($object instanceof ConfigAAwareInterface) {
        $object->setConfigA($configA);
    }
});
// ... add more injectors

现在要实际构造一个命令,你可以简单地调用:

$injector->inject(new CommandA());

并且注入器会根据实现的接口注入依赖。 乍一看,这可能有点复杂,但实际上有时很有帮助。 但是,如果您有多个需要注入的同一类的对象(例如 new Config("path/to/a.cfg") 和 new Config("path/to/b.cfg")),这可能不是一个理想的解决方案,因为您只能通过接口来区分。

依赖注入库

您当然也可以使用整个库并将其添加为依赖项。我在单独的答案中写了list of PHP dependency injection containers

【讨论】:

  • 我可以详细说明设计替代方案,如果您想要一些。只需发表评论。
  • @FalKeller - 如果您有想法可以添加,我将非常感谢任何对我开放的选项。正如您在我的代码 sn-ps 中看到的命令都是动态加载的,我可以为配置实现类似的机制,即:有一个配置文件的名称与它的命令完全相同,即:TestCommand.php、TestConfig。 php,如果该类存在,我将其设置在 $configuration...?只是在这里大声思考,我愿意接受所有建议。
  • 你摇滚,先生。我非常感谢您的洞察力。
猜你喜欢
  • 2011-11-27
  • 1970-01-01
  • 2014-11-08
  • 2021-05-09
  • 2013-10-19
  • 2012-10-22
  • 2022-11-20
  • 2015-04-02
  • 2014-11-15
相关资源
最近更新 更多