Symfony AutoBind Parameter is Dead, Long live Constant Parameters

I wrote the Do you Autowire Services in Symfony? You can Autowire Parameters Too almost 2 years ago. It seemed like a good idea at that time, to save manual YAML config wiring.

Now, with PHP configs on the Symfony markets, auto bind parameters became obsolete.

Welcome constant parameters.

In the past, we used YAML to define parameters for Symfony application:

parameters:
    location: Prague

How do we get parameter in a service? Via name auto binding: the parameter name = the param name in __construct:

final class SomeService
{
    private string $location;

    public function __construct(string $location)
    {
        $this->location = $location;

        dump($location); // "Prague"
    }
}

This allows us to have a typed parameter right in the constructor. We know it's a string.

YAML Without Knowledge

$valuesObjects = [
    new SomeService('Prague'),
    new SomeService('Berlin'),
    new SomeService('London'),
];

We don't know. We can be either anxious about it or not care about this detail at all.


Or we could quickly know with 1 click in PHPStorm. How?

Welcome Constant Parameters

With Symfony configs in *.php format, this is easypick:

use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return function (ContainerConfigurator $containerConfigurator): void {
    $parameters = $containerConfigurator->parameters();
    $parameters->set('location', 'Prague');
};

Wait, this was just a string. We want constant parameters:

use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use App\Configuration\Option;

return function (ContainerConfigurator $containerConfigurator): void {
    $parameters = $containerConfigurator->parameters();
    $parameters->set(Option::LOCATION, 'Prague');
};

When Constant Parameter and Service Parameter Meet

We still want to avoid service configuration... we have 2 options.

use App\Configuration\Option;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;

final class SomeService
{
    private string $location;

    public function __construct(ParameterBagInterface $parameterBag)
    {
        // re-type to (string) is needed, because "get()" return mixed type
        $this->location = (string) $parameterBag->get(Option::LOCATION);
    }
}
use App\Configuration\Option;
use Symplify\PackageBuilder\Parameter\ParameterProvider;

final class SomeService
{
    private string $location;

    public function __construct(ParameterProvider $parameterProvider)
    {
        // no re-type needed + parameter type validation included inside the ParameterProvider service
        $this->location = $parameterProvider->provideStringParameter(Option::LOCATION);
    }
}

Both services work to find first is out of the box, second is mutable and useful for testing or post-container configurations. Pick the one that suits your project better.

Are you unsure? Use the Symfony ParameterBagInterface.

One More Thing...

We now have a clean design and constant parameters without any name <=> name hacking. That's nice.

But the best is yet to come. In the previous version, we were missing essential information about the parameter:

Where are the places the parameter is used in?

Well, why not just click on the constant in your IDE?



Happy coding!




Do you learn from my contents or use open-souce packages like Rector every day?
Consider supporting it on GitHub Sponsors. I'd really appreciate it!