-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
Description
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
- 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]);
};
- 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);
}
}
- Ask the container to instantiate
DemoService
. - While
app.some_parameter
can be resolved, the container will warn about the absence ofapp.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()
:
symfony/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
Lines 1516 to 1522 in 1dcb0f9
$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:
symfony/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
Lines 1918 to 1934 in 1dcb0f9
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