tl;dr:我有一个 Symfony 服务配置,它将默认字符串值绑定到命名参数,我想在控制台进程中使用命令行选项值覆盖它。
我正在开发一个 Composer 库(php-tuf/composer-stager),它为类提供公共接口和一个为它们提供 shell 接口的 Symfony 控制台应用程序。对于代码 API,我想将默认(字符串)值绑定到一些服务参数,以便客户端不必在每次调用我的域服务时都包含参数,但他们仍然可以在自己的服务配置中覆盖默认值。
但是在我自己的控制台应用程序中,我希望能够根据用户可能提供的命令行选项覆盖这些默认值。例如,我希望有一个$string
默认值lorem
,但我希望我的控制台使用一个选项覆盖默认值,例如--string=ipsum
. 但是当控制台应用程序处理用户输入时,我找不到任何地方来更改容器中参数的值。这是一个简化的代码示例:
<?php
// bin/example.php, my console command front script
$containerBuilder = new ContainerBuilder();
$loader = new YamlFileLoader($containerBuilder, new FileLocator());
$loader->load(__DIR__ . '/../config/services.yml');
// This is the last chance to override defaults before the container
// gets compiled, but I'm reluctant to do so because I would have
// to parse the command line options myself, which would be ugly if
// not unwise.
$containerBuilder->compile();
$application = $containerBuilder->get(Application::class);
return $application->run();
# config/services.yml
services:
_defaults:
bind:
$string: lorem
# These classes constitute my public interface,
# and they take the $string parameter.
My\App\Domain\:
resource: '../src/Domain/*'
# These are the Console commands. They need to
# change the value of $string before it gets
# injected into the domain classes above.
My\App\Console\Commands\:
resource: '../src/Console/Commands/*'
<?php
// src/Domain/Example.php
// By the time this class is instantiated, the container has already been
// compiled, and it's received the $string variable from the services default.
class Example
{
public function __construct(string $string)
{
$this->string = $string;
}
public function printString()
{
print $this->string;
}
}
// src/Console/Application.php
// My Applilcation class is the first place (I think) I have access to
// console command input, but the container has already been compiled and
// services injected into my domain objects by the time it's loaded.
final class Application extends \Symfony\Component\Console\Application {}
<?php
// src/Console/Command/ExampleCommand.php
class ExampleCommand
{
public function __construct(Example $example)
{
// At this point the Example class has ALREADY had the
// $string value injected from the services config.
$this->example = $example;
}
public function execute(InputInterface $input, OutputInterface $output): int
{
// ...but I don't get access to the '--override' option until here.
$override = $input->getOption('override');
// I'm not WILLING to take $string as a method parameter like this:
$this->example->printString($override);
// I don't really WANT to add a setter method to the domain (Example)
// class: It would be too easy for a developer to forget to do this in
// new commands and introduce bugs.
$this->example->setString($override);
$this->example->printString();
// I want the Example class to have gotten the overridden value in
// the first place so I can call it without every class having to
// worry about it:
$this->example->printString();
}
}
有任何想法吗?
您可以在https://github.com/php-tuf/composer-stager/tree/v0.1.0查看我当前的代码库。
注意:我的应用程序此时需要支持 Symfony 4 和 5。