Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[DependencyInjection] Unable to inject parameters containing array of enums into service #49505

Closed
@imba28

Description

@imba28

Symfony version(s) affected

^6.2.0

Description

Symfony 6.2 introduced enums as service parameters: https://symfony.com/blog/new-in-symfony-6-2-improved-enum-support
One of the examples includes defining an array of enums as a parameter:

// config/services.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use App\Entity\BlogPost;

return static function (ContainerConfigurator $container) {
    $container->parameters()
        // ...

        ->set('app.some_parameter', SomeEnum::Foo)
        ->set('app.another_parameter', [SomeEnum::Foo, SomeEnum::Bar]);
};

Although, when you try to inject said parameter into a service, the container produces a warning when it's asked to instantiate the service:

Warning: Undefined array key "app.another_parameter"

How to reproduce

  1. Define an array of enum as a service parameter, as stated in the documentation.
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use App\SomeEnum;

return static function (ContainerConfigurator $container) {
    $container->parameters()
        // ...

        ->set('app.some_parameter', SomeEnum::Foo)
        ->set('app.another_parameter', [SomeEnum::Foo, SomeEnum::Bar]);
};
  1. Next, create a class and inject both of the parameters into the constructor method:
namespace App\Service;

use App\SomeEnum;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

final class DemoService
{
    public function __construct(
        #[Autowire('%app.some_parameter%')]
        private SomeEnum $enum,

        #[Autowire('%app.another_parameter%')]
        private array $enums
    )
    {
        dd($this->enum, $this->enums);
    }
}
  1. Ask the container to instantiate DemoService.
  2. While app.some_parameter can be resolved, the container will warn about the absence of app.another_parameter.

I created a repo demonstrating this issue: See https://github.com/imba28/symfony-enum-array-parameters-bug for a complete example

Possible Solution

I think this is due to a bug in the class PhpDumper. Whenever a parameter contains an enum, it is marked as a dynamic parameter that must be resolved using $container->getParameter():

$hasEnum = false;
$export = $this->exportParameters([$value], '', 12, $hasEnum);
$export = explode('0 => ', substr(rtrim($export, " ]\n"), 2, -1), 2);
if ($hasEnum || preg_match("/\\\$this->(?:getEnv\('(?:[-.\w\\\\]*+:)*+\w++'\)|targetDir\.'')/", $export[1])) {
$dynamicPhp[$key] = sprintf('%s%s => %s,', $export[0], $this->export($key), $export[1]);
} else {

However, the routine dumping the parameters lacks an equivalent check:

private function dumpParameter(string $name): string
{
if ($this->container->hasParameter($name)) {
$value = $this->container->getParameter($name);
$dumpedValue = $this->dumpValue($value, false);
if (!$value || !\is_array($value)) {
return $dumpedValue;
}
if (!preg_match("/\\\$this->(?:getEnv\('(?:[-.\w\\\\]*+:)*+\w++'\)|targetDir\.'')/", $dumpedValue)) {
return sprintf('$this->parameters[%s]', $this->doExport($name));
}
}
return sprintf('$this->getParameter(%s)', $this->doExport($name));
}

For that reason, the generated file var/cache/dev/ContainerXYZ/getDemoServiceService.php includes

return $container->privates['App\\Service\\DemoService'] = new \App\Service\DemoService(\App\SomeEnum::Foo, $container->parameters['app.another_parameter']);

instead of

return $container->privates['App\\Service\\DemoService'] = new \App\Service\DemoService(\App\SomeEnum::Foo, $container->getParameter('app.another_parameter'));

which eventually fails, since app.another_parameter has been previously marked as a dynamic parameter and thus is not part of $container->parameters.

Possible fix

Recursively traverse $value and determine if there's an enum hidden somewhere and place the check in this condition: https://github.com/symfony/symfony/blob/6.2/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php#L1928

If it does, dump $this->getParameter(%s) instead of $this->parameters[%s].

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions