tl;dr: У меня есть конфигурация сервиса Symfony, которая связывает строковое значение по умолчанию с именованным параметром, и я хочу переопределить его в процессе консоли с помощью значения параметра командной строки.
Я работаю над библиотекой Composer (php-tuf/composer-stager), которая предоставляет классы с открытыми интерфейсами и консольным приложением 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.
Я бы использовал метод установки для класса домена. Или, глядя на репозиторий, вы найдете способ переопределить в AbstractCommand, чтобы разработчики, которые просто расширяют его, не беспокоились о том, чтобы забыть
Могу поклясться, что прокомментировал это вчера. Ну что ж. После того, как контейнер скомпилирован, вы не можете вносить в него изменения. Конец истории. Вместо этого рассмотрите возможность использования ExampleFactory.