TravisCarden
TravisCarden

Reputation: 387

Is there a way to override a Symfony container value with a Console argument?

tl;dr: I have a Symfony service configuration that binds a default string value to a named parameter, and I want to override it in a Console process with a command line option value.

I'm working on a Composer library (php-tuf/composer-stager) that provides classes with public interfaces and a Symfony Console app that provides a shell interface to them. For the the code API I want to bind default (string) values to a few service arguments so that clients don't have to include the arguments in every call to my domain services but they can still override the defaults in their own services configuration.

But in my own console application, I want to be able to override those defaults based on command line options the user may supply. For example, I want to have a $string default value of lorem, but I want my console to override the default with an option, e.g., --string=ipsum. But by the time the Console app processes the user input I can't find anywhere to change the value of the parameter in the container. Here's a simplified code sample:

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

Any ideas?

You can see my current codebase at https://github.com/php-tuf/composer-stager/tree/v0.1.0.

Note: My app needs to support Symfony 4 and 5 at this time.

Upvotes: 2

Views: 442

Answers (0)

Related Questions