Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

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

Copy link
Copy link
Closed
@imba28

Description

@imba28
Issue body actions

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

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.