From 0f0a2fbfae6cced89e2c3fe2cf12f978159649c5 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Thu, 29 Sep 2022 08:26:22 +0200 Subject: [PATCH] [DependencyInjection] Enable deprecating parameters --- .../DependencyInjection/CHANGELOG.md | 1 + .../DependencyInjection/Container.php | 6 +- .../DependencyInjection/ContainerBuilder.php | 31 ++- .../DependencyInjection/Dumper/PhpDumper.php | 39 +++- .../ParameterBag/FrozenParameterBag.php | 13 +- .../ParameterBag/ParameterBag.php | 26 ++- .../Tests/ContainerBuilderTest.php | 106 +++++++++ .../Tests/Dumper/PhpDumperTest.php | 35 +++ .../container_deprecated_parameters.php | 12 ++ .../Fixtures/php/services10_as_files.txt | 6 +- .../Tests/Fixtures/php/services9_as_files.txt | 6 +- .../php/services9_inlined_factories.txt | 6 +- .../php/services9_lazy_inlined_factories.txt | 6 +- .../php/services_deprecated_parameters.php | 110 ++++++++++ ...ervices_deprecated_parameters_as_files.txt | 202 ++++++++++++++++++ .../php/services_non_shared_lazy_as_files.txt | 6 +- .../ParameterBag/FrozenParameterBagTest.php | 27 +++ .../Tests/ParameterBag/ParameterBagTest.php | 73 +++++++ 18 files changed, 671 insertions(+), 40 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_deprecated_parameters.php create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_deprecated_parameters.php create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_deprecated_parameters_as_files.txt diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index df045b56f05c8..d40f439080c34 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG * Add support for nesting autowiring-related attributes into `#[Autowire(...)]` * Deprecate undefined and numeric keys with `service_locator` config * Fail if Target attribute does not exist during compilation + * Enable deprecating parameters with `ContainerBuilder::deprecateParameter()` 6.2 --- diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php index 7b4c8ccf88ae5..ff10702229dc6 100644 --- a/src/Symfony/Component/DependencyInjection/Container.php +++ b/src/Symfony/Component/DependencyInjection/Container.php @@ -22,6 +22,7 @@ use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Contracts\Service\ResetInterface; @@ -82,7 +83,10 @@ public function compile() { $this->parameterBag->resolve(); - $this->parameterBag = new FrozenParameterBag($this->parameterBag->all()); + $this->parameterBag = new FrozenParameterBag( + $this->parameterBag->all(), + $this->parameterBag instanceof ParameterBag ? $this->parameterBag->allDeprecated() : [] + ); $this->compiled = true; } diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 63e288adc63b3..9f92ad8c295a7 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -34,6 +34,7 @@ use Symfony\Component\DependencyInjection\Exception\BadMethodCallException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\LogicException; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; @@ -610,7 +611,15 @@ public function merge(self $container) } } $this->addAliases($container->getAliases()); - $this->getParameterBag()->add($container->getParameterBag()->all()); + $parameterBag = $this->getParameterBag(); + $otherBag = $container->getParameterBag(); + $parameterBag->add($otherBag->all()); + + if ($parameterBag instanceof ParameterBag && $otherBag instanceof ParameterBag) { + foreach ($otherBag->allDeprecated() as $name => $deprecated) { + $parameterBag->deprecate($name, ...$deprecated); + } + } if ($this->trackResources) { foreach ($container->getResources() as $resource) { @@ -626,9 +635,9 @@ public function merge(self $container) $this->extensionConfigs[$name] = array_merge($this->extensionConfigs[$name], $container->getExtensionConfig($name)); } - if ($this->getParameterBag() instanceof EnvPlaceholderParameterBag && $container->getParameterBag() instanceof EnvPlaceholderParameterBag) { - $envPlaceholders = $container->getParameterBag()->getEnvPlaceholders(); - $this->getParameterBag()->mergeEnvPlaceholders($container->getParameterBag()); + if ($parameterBag instanceof EnvPlaceholderParameterBag && $otherBag instanceof EnvPlaceholderParameterBag) { + $envPlaceholders = $otherBag->getEnvPlaceholders(); + $parameterBag->mergeEnvPlaceholders($otherBag); } else { $envPlaceholders = []; } @@ -689,6 +698,20 @@ public function prependExtensionConfig(string $name, array $config) array_unshift($this->extensionConfigs[$name], $config); } + /** + * Deprecates a service container parameter. + * + * @throws ParameterNotFoundException if the parameter is not defined + */ + public function deprecateParameter(string $name, string $package, string $version, string $message = 'The parameter "%s" is deprecated.'): void + { + if (!$this->parameterBag instanceof ParameterBag) { + throw new BadMethodCallException(sprintf('The parameter bag must be an instance of "%s" to call "%s".', ParameterBag::class, __METHOD__)); + } + + $this->parameterBag->deprecate($name, $package, $version, $message); + } + /** * Compiles the container. * diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 5ae2541729a9b..5753524c1ffab 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -35,6 +35,7 @@ use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper; use Symfony\Component\DependencyInjection\Loader\FileLoader; use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ServiceLocator as BaseServiceLocator; use Symfony\Component\DependencyInjection\TypedReference; @@ -1234,6 +1235,8 @@ private function startClass(string $class, string $baseClass, bool $hasProxyClas */ class $class extends $baseClass { + private const DEPRECATED_PARAMETERS = []; + protected \$parameters = []; protected readonly \WeakReference \$ref; @@ -1242,11 +1245,9 @@ public function __construct() \$this->ref = \WeakReference::create(\$this); EOF; + $code = str_replace(" private const DEPRECATED_PARAMETERS = [];\n\n", $this->addDeprecatedParameters(), $code); if ($this->asFiles) { - $code = str_replace('$parameters = []', "\$containerDir;\n protected \$parameters = [];\n private \$buildParameters", $code); - $code = str_replace('__construct()', '__construct(array $buildParameters = [], $containerDir = __DIR__)', $code); - $code .= " \$this->buildParameters = \$buildParameters;\n"; - $code .= " \$this->containerDir = \$containerDir;\n"; + $code = str_replace('__construct()', '__construct(private array $buildParameters = [], protected string $containerDir = __DIR__)', $code); if (null !== $this->targetDirRegex) { $code = str_replace('$parameters = []', "\$targetDir;\n protected \$parameters = []", $code); @@ -1391,6 +1392,24 @@ public function getRemovedIds(): array EOF; } + private function addDeprecatedParameters(): string + { + if (!($bag = $this->container->getParameterBag()) instanceof ParameterBag) { + return ''; + } + + if (!$deprecated = $bag->allDeprecated()) { + return ''; + } + $code = ''; + ksort($deprecated); + foreach ($deprecated as $param => $deprecation) { + $code .= ' '.$this->doExport($param).' => ['.implode(', ', array_map($this->doExport(...), $deprecation))."],\n"; + } + + return " private const DEPRECATED_PARAMETERS = [\n{$code} ];\n\n"; + } + private function addMethodMap(): string { $code = ''; @@ -1552,6 +1571,10 @@ private function addDefaultParametersMethod(): string public function getParameter(string $name): array|bool|string|int|float|\UnitEnum|null { + if (isset(self::DEPRECATED_PARAMETERS[$name])) { + trigger_deprecation(...self::DEPRECATED_PARAMETERS[$name]); + } + if (isset($this->buildParameters[$name])) { return $this->buildParameters[$name]; } @@ -1590,17 +1613,23 @@ public function getParameterBag(): ParameterBagInterface foreach ($this->buildParameters as $name => $value) { $parameters[$name] = $value; } - $this->parameterBag = new FrozenParameterBag($parameters); + $this->parameterBag = new FrozenParameterBag($parameters, self::DEPRECATED_PARAMETERS); } return $this->parameterBag; } EOF; + if (!$this->asFiles) { $code = preg_replace('/^.*buildParameters.*\n.*\n.*\n\n?/m', '', $code); } + if (!($bag = $this->container->getParameterBag()) instanceof ParameterBag || !$bag->allDeprecated()) { + $code = preg_replace("/\n.*DEPRECATED_PARAMETERS.*\n.*\n.*\n/m", '', $code, 1); + $code = str_replace(', self::DEPRECATED_PARAMETERS', '', $code); + } + if ($dynamicPhp) { $loadedDynamicParameters = $this->exportParameters(array_combine(array_keys($dynamicPhp), array_fill(0, \count($dynamicPhp), false)), '', 8); $getDynamicParameter = <<<'EOF' diff --git a/src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php b/src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php index 4e0f4bc2e929d..d4933af33e8aa 100644 --- a/src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php +++ b/src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php @@ -25,11 +25,11 @@ class FrozenParameterBag extends ParameterBag * all keys are already lowercased. * * This is always the case when used internally. - * - * @param array $parameters An array of parameters */ - public function __construct(array $parameters = []) - { + public function __construct( + array $parameters = [], + protected array $deprecatedParameters = [], + ) { $this->parameters = $parameters; $this->resolved = true; } @@ -49,6 +49,11 @@ public function set(string $name, array|bool|string|int|float|\UnitEnum|null $va throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); } + public function deprecate(string $name, string $package, string $version, string $message = 'The parameter "%s" is deprecated.') + { + throw new LogicException('Impossible to call deprecate() on a frozen ParameterBag.'); + } + public function remove(string $name) { throw new LogicException('Impossible to call remove() on a frozen ParameterBag.'); diff --git a/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php b/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php index ece5c3f45cdde..97656011d3fbd 100644 --- a/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php +++ b/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php @@ -24,6 +24,7 @@ class ParameterBag implements ParameterBagInterface { protected $parameters = []; protected $resolved = false; + protected array $deprecatedParameters = []; public function __construct(array $parameters = []) { @@ -47,6 +48,11 @@ public function all(): array return $this->parameters; } + public function allDeprecated(): array + { + return $this->deprecatedParameters; + } + public function get(string $name): array|bool|string|int|float|\UnitEnum|null { if (!\array_key_exists($name, $this->parameters)) { @@ -81,6 +87,10 @@ public function get(string $name): array|bool|string|int|float|\UnitEnum|null throw new ParameterNotFoundException($name, null, null, null, $alternatives, $nonNestedAlternative); } + if (isset($this->deprecatedParameters[$name])) { + trigger_deprecation(...$this->deprecatedParameters[$name]); + } + return $this->parameters[$name]; } @@ -95,6 +105,20 @@ public function set(string $name, array|bool|string|int|float|\UnitEnum|null $va $this->parameters[$name] = $value; } + /** + * Deprecates a service container parameter. + * + * @throws ParameterNotFoundException if the parameter is not defined + */ + public function deprecate(string $name, string $package, string $version, string $message = 'The parameter "%s" is deprecated.') + { + if (!\array_key_exists($name, $this->parameters)) { + throw new ParameterNotFoundException($name); + } + + $this->deprecatedParameters[$name] = [$package, $version, $message, $name]; + } + public function has(string $name): bool { return \array_key_exists($name, $this->parameters); @@ -102,7 +126,7 @@ public function has(string $name): bool public function remove(string $name) { - unset($this->parameters[$name]); + unset($this->parameters[$name], $this->deprecatedParameters[$name]); } public function resolve() diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 9ec5d0631fe61..dd47799261b5f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -31,9 +31,11 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\BadMethodCallException; use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; @@ -42,6 +44,7 @@ use Symfony\Component\DependencyInjection\Loader\ClosureLoader; use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\DependencyInjection\Tests\Compiler\Foo; @@ -100,6 +103,109 @@ public function testDefinitions() } } + /** + * The test should be kept in the group as it always expects a deprecation. + * + * @group legacy + */ + public function testDeprecateParameter() + { + $builder = new ContainerBuilder(); + $builder->setParameter('foo', 'bar'); + + $builder->deprecateParameter('foo', 'symfony/test', '6.3'); + + $this->expectDeprecation('Since symfony/test 6.3: The parameter "foo" is deprecated.'); + + $builder->getParameter('foo'); + } + + /** + * The test should be kept in the group as it always expects a deprecation. + * + * @group legacy + */ + public function testParameterDeprecationIsTrgiggeredWhenCompiled() + { + $builder = new ContainerBuilder(); + $builder->setParameter('foo', '%bar%'); + $builder->setParameter('bar', 'baz'); + + $builder->deprecateParameter('bar', 'symfony/test', '6.3'); + + $this->expectDeprecation('Since symfony/test 6.3: The parameter "bar" is deprecated.'); + + $builder->compile(); + } + + public function testDeprecateParameterThrowsWhenParameterIsUndefined() + { + $builder = new ContainerBuilder(); + + $this->expectException(ParameterNotFoundException::class); + $this->expectExceptionMessage('You have requested a non-existent parameter "foo".'); + + $builder->deprecateParameter('foo', 'symfony/test', '6.3'); + } + + public function testDeprecateParameterThrowsWhenParameterBagIsNotInternal() + { + $builder = new ContainerBuilder(new class() implements ParameterBagInterface { + public function clear() + { + } + + public function add(array $parameters) + { + } + + public function all(): array + { + return []; + } + + public function get(string $name): array|bool|string|int|float|\UnitEnum|null + { + return null; + } + + public function remove(string $name) + { + } + + public function set(string $name, \UnitEnum|float|int|bool|array|string|null $value) + { + } + + public function has(string $name): bool + { + return false; + } + + public function resolve() + { + } + + public function resolveValue(mixed $value) + { + } + + public function escapeValue(mixed $value): mixed + { + return null; + } + + public function unescapeValue(mixed $value): mixed + { + return null; + } + }); + + $this->expectException(BadMethodCallException::class); + + $builder->deprecateParameter('foo', 'symfony/test', '6.3'); + } + public function testRegister() { $builder = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 2c25b1fd90d71..0a80761610aeb 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -451,6 +451,41 @@ public function testDumpAutowireData() $this->assertStringEqualsFile(self::$fixturesPath.'/php/services24.php', $dumper->dump()); } + /** + * The test should be kept in the group as it always expects a deprecation. + * + * @group legacy + */ + public function testDeprecatedParameters() + { + $container = include self::$fixturesPath.'/containers/container_deprecated_parameters.php'; + + $this->expectDeprecation('Since symfony/test 6.3: The parameter "foo_class" is deprecated.'); + $container->compile(); + + $dumper = new PhpDumper($container); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_deprecated_parameters.php', $dumper->dump()); + } + + /** + * The test should be kept in the group as it always expects a deprecation. + * + * @group legacy + */ + public function testDeprecatedParametersAsFiles() + { + $container = include self::$fixturesPath.'/containers/container_deprecated_parameters.php'; + + $this->expectDeprecation('Since symfony/test 6.3: The parameter "foo_class" is deprecated.'); + $container->compile(); + + $dumper = new PhpDumper($container); + $dump = print_r($dumper->dump(['as_files' => true, 'file' => __DIR__, 'inline_factories_parameter' => false, 'inline_class_loader_parameter' => false]), true); + + $this->assertStringMatchesFormatFile(self::$fixturesPath.'/php/services_deprecated_parameters_as_files.txt', $dump); + } + public function testEnvInId() { $container = include self::$fixturesPath.'/containers/container_env_in_id.php'; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_deprecated_parameters.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_deprecated_parameters.php new file mode 100644 index 0000000000000..96b0f74c59759 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_deprecated_parameters.php @@ -0,0 +1,12 @@ +setParameter('foo_class', 'FooClass\\Foo'); +$container->deprecateParameter('foo_class', 'symfony/test', '6.3'); +$container->register('foo', '%foo_class%') + ->setPublic(true) +; + +return $container; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10_as_files.txt index b7c31e8137aa4..5cbb72f802159 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10_as_files.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10_as_files.txt @@ -59,17 +59,13 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; */ class ProjectServiceContainer extends Container { - protected $containerDir; protected $targetDir; protected $parameters = []; - private $buildParameters; protected readonly \WeakReference $ref; - public function __construct(array $buildParameters = [], $containerDir = __DIR__) + public function __construct(private array $buildParameters = [], protected string $containerDir = __DIR__) { $this->ref = \WeakReference::create($this); - $this->buildParameters = $buildParameters; - $this->containerDir = $containerDir; $this->targetDir = \dirname($containerDir); $this->services = $this->privates = []; $this->methodMap = [ diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt index 1321a66ec1bce..461787826da66 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt @@ -563,17 +563,13 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; */ class ProjectServiceContainer extends Container { - protected $containerDir; protected $targetDir; protected $parameters = []; - private $buildParameters; protected readonly \WeakReference $ref; - public function __construct(array $buildParameters = [], $containerDir = __DIR__) + public function __construct(private array $buildParameters = [], protected string $containerDir = __DIR__) { $this->ref = \WeakReference::create($this); - $this->buildParameters = $buildParameters; - $this->containerDir = $containerDir; $this->targetDir = \dirname($containerDir); $this->parameters = $this->getDefaultParameters(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_inlined_factories.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_inlined_factories.txt index 920e4036507a4..b561ff647f05c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_inlined_factories.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_inlined_factories.txt @@ -36,17 +36,13 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; */ class ProjectServiceContainer extends Container { - protected $containerDir; protected $targetDir; protected $parameters = []; - private $buildParameters; protected readonly \WeakReference $ref; - public function __construct(array $buildParameters = [], $containerDir = __DIR__) + public function __construct(private array $buildParameters = [], protected string $containerDir = __DIR__) { $this->ref = \WeakReference::create($this); - $this->buildParameters = $buildParameters; - $this->containerDir = $containerDir; $this->targetDir = \dirname($containerDir); $this->parameters = $this->getDefaultParameters(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt index 4944c13961691..965dc91661cce 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt @@ -31,17 +31,13 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; */ class ProjectServiceContainer extends Container { - protected $containerDir; protected $targetDir; protected $parameters = []; - private $buildParameters; protected readonly \WeakReference $ref; - public function __construct(array $buildParameters = [], $containerDir = __DIR__) + public function __construct(private array $buildParameters = [], protected string $containerDir = __DIR__) { $this->ref = \WeakReference::create($this); - $this->buildParameters = $buildParameters; - $this->containerDir = $containerDir; $this->targetDir = \dirname($containerDir); $this->parameters = $this->getDefaultParameters(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_deprecated_parameters.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_deprecated_parameters.php new file mode 100644 index 0000000000000..2ed3522f6f578 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_deprecated_parameters.php @@ -0,0 +1,110 @@ + ['symfony/test', '6.3', 'The parameter "%s" is deprecated.', 'foo_class'], + ]; + + protected $parameters = []; + protected readonly \WeakReference $ref; + + public function __construct() + { + $this->ref = \WeakReference::create($this); + $this->parameters = $this->getDefaultParameters(); + + $this->services = $this->privates = []; + $this->methodMap = [ + 'foo' => 'getFooService', + ]; + + $this->aliases = []; + } + + public function compile(): void + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled(): bool + { + return true; + } + + /** + * Gets the public 'foo' shared service. + * + * @return \FooClass\Foo + */ + protected static function getFooService($container) + { + return $container->services['foo'] = new \FooClass\Foo(); + } + + public function getParameter(string $name): array|bool|string|int|float|\UnitEnum|null + { + if (isset(self::DEPRECATED_PARAMETERS[$name])) { + trigger_deprecation(...self::DEPRECATED_PARAMETERS[$name]); + } + + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) { + throw new ParameterNotFoundException($name); + } + if (isset($this->loadedDynamicParameters[$name])) { + return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + + return $this->parameters[$name]; + } + + public function hasParameter(string $name): bool + { + return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters); + } + + public function setParameter(string $name, $value): void + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + public function getParameterBag(): ParameterBagInterface + { + if (null === $this->parameterBag) { + $parameters = $this->parameters; + foreach ($this->loadedDynamicParameters as $name => $loaded) { + $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + $this->parameterBag = new FrozenParameterBag($parameters, self::DEPRECATED_PARAMETERS); + } + + return $this->parameterBag; + } + + private $loadedDynamicParameters = []; + private $dynamicParameters = []; + + private function getDynamicParameter(string $name) + { + throw new ParameterNotFoundException($name); + } + + protected function getDefaultParameters(): array + { + return [ + 'foo_class' => 'FooClass\\Foo', + ]; + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_deprecated_parameters_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_deprecated_parameters_as_files.txt new file mode 100644 index 0000000000000..7d2af40ebda4d --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_deprecated_parameters_as_files.txt @@ -0,0 +1,202 @@ +Array +( + [Container%s/getFooService.php] => services['foo'] = new \FooClass\Foo(); + } +} + + [Container%s/ProjectServiceContainer.php] => ['symfony/test', '6.3', 'The parameter "%s" is deprecated.', 'foo_class'], + ]; + + protected $targetDir; + protected $parameters = []; + protected readonly \WeakReference $ref; + + public function __construct(private array $buildParameters = [], protected string $containerDir = __DIR__) + { + $this->ref = \WeakReference::create($this); + $this->targetDir = \dirname($containerDir); + $this->parameters = $this->getDefaultParameters(); + + $this->services = $this->privates = []; + $this->fileMap = [ + 'foo' => 'getFooService', + ]; + + $this->aliases = []; + } + + public function compile(): void + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled(): bool + { + return true; + } + + protected function load($file, $lazyLoad = true) + { + if (class_exists($class = __NAMESPACE__.'\\'.$file, false)) { + return $class::do($this, $lazyLoad); + } + + if ('.' === $file[-4]) { + $class = substr($class, 0, -4); + } else { + $file .= '.php'; + } + + $service = require $this->containerDir.\DIRECTORY_SEPARATOR.$file; + + return class_exists($class, false) ? $class::do($this, $lazyLoad) : $service; + } + + public function getParameter(string $name): array|bool|string|int|float|\UnitEnum|null + { + if (isset(self::DEPRECATED_PARAMETERS[$name])) { + trigger_deprecation(...self::DEPRECATED_PARAMETERS[$name]); + } + + if (isset($this->buildParameters[$name])) { + return $this->buildParameters[$name]; + } + + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) { + throw new ParameterNotFoundException($name); + } + if (isset($this->loadedDynamicParameters[$name])) { + return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + + return $this->parameters[$name]; + } + + public function hasParameter(string $name): bool + { + if (isset($this->buildParameters[$name])) { + return true; + } + + return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters); + } + + public function setParameter(string $name, $value): void + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + public function getParameterBag(): ParameterBagInterface + { + if (null === $this->parameterBag) { + $parameters = $this->parameters; + foreach ($this->loadedDynamicParameters as $name => $loaded) { + $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + foreach ($this->buildParameters as $name => $value) { + $parameters[$name] = $value; + } + $this->parameterBag = new FrozenParameterBag($parameters, self::DEPRECATED_PARAMETERS); + } + + return $this->parameterBag; + } + + private $loadedDynamicParameters = []; + private $dynamicParameters = []; + + private function getDynamicParameter(string $name) + { + throw new ParameterNotFoundException($name); + } + + protected function getDefaultParameters(): array + { + return [ + 'foo_class' => 'FooClass\\Foo', + ]; + } +} + + [ProjectServiceContainer.preload.php] => = 7.4 when preloading is desired + +use Symfony\Component\DependencyInjection\Dumper\Preloader; + +if (in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) { + return; +} + +require dirname(__DIR__, %d)%svendor/autoload.php'; +(require __DIR__.'/ProjectServiceContainer.php')->set(\Container%s\ProjectServiceContainer::class, null); +require __DIR__.'/Container%s/getFooService.php'; + +$classes = []; +$classes[] = 'FooClass\Foo'; +$classes[] = 'Symfony\Component\DependencyInjection\ContainerInterface'; + +$preloaded = Preloader::preload($classes); + + [ProjectServiceContainer.php] => '%s', + 'container.build_id' => '%s', + 'container.build_time' => %d, +], __DIR__.\DIRECTORY_SEPARATOR.'Container%s'); + +) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt index 0594c76789555..f7436c455e450 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt @@ -77,17 +77,13 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; */ class ProjectServiceContainer extends Container { - protected $containerDir; protected $targetDir; protected $parameters = []; - private $buildParameters; protected readonly \WeakReference $ref; - public function __construct(array $buildParameters = [], $containerDir = __DIR__) + public function __construct(private array $buildParameters = [], protected string $containerDir = __DIR__) { $this->ref = \WeakReference::create($this); - $this->buildParameters = $buildParameters; - $this->containerDir = $containerDir; $this->targetDir = \dirname($containerDir); $this->services = $this->privates = []; $this->fileMap = [ diff --git a/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/FrozenParameterBagTest.php b/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/FrozenParameterBagTest.php index 40630364d8d3c..792a9c2455cef 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/FrozenParameterBagTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/FrozenParameterBagTest.php @@ -12,10 +12,13 @@ namespace Symfony\Component\DependencyInjection\Tests\ParameterBag; use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag; class FrozenParameterBagTest extends TestCase { + use ExpectDeprecationTrait; + public function testConstructor() { $parameters = [ @@ -53,4 +56,28 @@ public function testRemove() $bag = new FrozenParameterBag(['foo' => 'bar']); $bag->remove('foo'); } + + public function testDeprecate() + { + $this->expectException(\LogicException::class); + $bag = new FrozenParameterBag(['foo' => 'bar']); + $bag->deprecate('foo', 'symfony/test', '6.3'); + } + + /** + * The test should be kept in the group as it always expects a deprecation. + * + * @group legacy + */ + public function testGetDeprecated() + { + $bag = new FrozenParameterBag( + ['foo' => 'bar'], + ['foo' => ['symfony/test', '6.3', 'The parameter "%s" is deprecated.', 'foo']] + ); + + $this->expectDeprecation('Since symfony/test 6.3: The parameter "foo" is deprecated.'); + + $bag->get('foo'); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/ParameterBagTest.php b/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/ParameterBagTest.php index e97ec063e52a8..201c557025e18 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/ParameterBagTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/ParameterBagTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Tests\ParameterBag; use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; @@ -19,6 +20,8 @@ class ParameterBagTest extends TestCase { + use ExpectDeprecationTrait; + public function testConstructor() { $bag = new ParameterBag($parameters = [ @@ -48,6 +51,18 @@ public function testRemove() $this->assertEquals(['bar' => 'bar'], $bag->all(), '->remove() removes a parameter'); } + public function testRemoveWithDeprecation() + { + $bag = new ParameterBag([ + 'foo' => 'foo', + 'bar' => 'bar', + ]); + $bag->deprecate('foo', 'symfony/test', '6.3'); + $bag->remove('foo'); + $this->assertEquals(['bar' => 'bar'], $bag->all(), '->remove() removes a parameter'); + $this->assertEquals([], $bag->allDeprecated()); + } + public function testGetSet() { $bag = new ParameterBag(['foo' => 'bar']); @@ -125,6 +140,64 @@ public function provideGetThrowParameterNotFoundExceptionData() ]; } + /** + * The test should be kept in the group as it always expects a deprecation. + * + * @group legacy + */ + public function testDeprecate() + { + $bag = new ParameterBag(['foo' => 'bar']); + + $bag->deprecate('foo', 'symfony/test', '6.3'); + + $this->expectDeprecation('Since symfony/test 6.3: The parameter "foo" is deprecated.'); + + $bag->get('foo'); + } + + /** + * The test should be kept in the group as it always expects a deprecation. + * + * @group legacy + */ + public function testDeprecateWithMessage() + { + $bag = new ParameterBag(['foo' => 'bar']); + + $bag->deprecate('foo', 'symfony/test', '6.3', 'The parameter "%s" is deprecated, use "new_foo" instead.'); + + $this->expectDeprecation('Since symfony/test 6.3: The parameter "foo" is deprecated, use "new_foo" instead.'); + + $bag->get('foo'); + } + + /** + * The test should be kept in the group as it always expects a deprecation. + * + * @group legacy + */ + public function testDeprecationIsTriggeredWhenResolved() + { + $bag = new ParameterBag(['foo' => '%bar%', 'bar' => 'baz']); + + $bag->deprecate('bar', 'symfony/test', '6.3'); + + $this->expectDeprecation('Since symfony/test 6.3: The parameter "bar" is deprecated.'); + + $bag->resolve(); + } + + public function testDeprecateThrowsWhenParameterIsUndefined() + { + $bag = new ParameterBag(); + + $this->expectException(ParameterNotFoundException::class); + $this->expectExceptionMessage('You have requested a non-existent parameter "foo".'); + + $bag->deprecate('foo', 'symfony/test', '6.3'); + } + public function testHas() { $bag = new ParameterBag(['foo' => 'bar']);