diff --git a/src/Symfony/Component/DependencyInjection/Alias.php b/src/Symfony/Component/DependencyInjection/Alias.php index 2b52986be87e4..e61084c6f0b59 100644 --- a/src/Symfony/Component/DependencyInjection/Alias.php +++ b/src/Symfony/Component/DependencyInjection/Alias.php @@ -86,10 +86,10 @@ public function isPrivate() } /** - * Whether this alias is deprecated, that means it should not be called + * Whether this alias is deprecated, that means it should not be referenced * anymore. * - * @param bool $status Defaults to true + * @param bool $status Whether this alias is deprecated, defaults to true * @param string $template Optional template message to use if the alias is deprecated * * @return $this @@ -115,24 +115,12 @@ public function setDeprecated($status = true, $template = null) return $this; } - /** - * Returns whether this alias is deprecated. - * - * @return bool - */ - public function isDeprecated() + public function isDeprecated(): bool { return $this->deprecated; } - /** - * Message to use if this alias is deprecated. - * - * @param string $id Service id relying on this alias - * - * @return string - */ - public function getDeprecationMessage($id) + public function getDeprecationMessage(string $id): string { return str_replace('%service_id%', $id, $this->deprecationTemplate ?: self::$defaultDeprecationTemplate); } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php index 7777243da96cc..3742d662486da 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php @@ -31,6 +31,7 @@ public function process(ContainerBuilder $container) foreach ($container->getAliases() as $id => $alias) { $aliasId = (string) $alias; + if ($aliasId !== $defId = $this->getDefinitionId($aliasId, $container)) { $container->setAlias($id, $defId)->setPublic($alias->isPublic())->setPrivate($alias->isPrivate()); } @@ -60,8 +61,15 @@ private function getDefinitionId(string $id, ContainerBuilder $container): strin if (isset($seen[$id])) { throw new ServiceCircularReferenceException($id, array_merge(array_keys($seen), [$id])); } + $seen[$id] = true; - $id = (string) $container->getAlias($id); + $alias = $container->getAlias($id); + + if ($alias->isDeprecated()) { + @trigger_error($alias->getDeprecationMessage($id), E_USER_DEPRECATED); + } + + $id = (string) $alias; } return $id; diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 999410fb2d160..25cef99de875a 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -579,12 +579,13 @@ private function doGet($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_ } if (!isset($this->definitions[$id]) && isset($this->aliasDefinitions[$id])) { - $aliasDefinition = $this->aliasDefinitions[$id]; - if ($aliasDefinition->isDeprecated()) { - @trigger_error($aliasDefinition->getDeprecationMessage($id), E_USER_DEPRECATED); + $alias = $this->aliasDefinitions[$id]; + + if ($alias->isDeprecated()) { + @trigger_error($alias->getDeprecationMessage($id), E_USER_DEPRECATED); } - return $this->doGet((string) $aliasDefinition, $invalidBehavior, $inlineServices, $isConstructorArgument); + return $this->doGet((string) $alias, $invalidBehavior, $inlineServices, $isConstructorArgument); } try { diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index b1b3ef9f0080c..473b34b261a4c 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -218,6 +218,7 @@ public function dump(array $options = []) $code = $this->startClass($options['class'], $baseClass, $baseClassWithNamespace). $this->addServices($services). + $this->addDeprecatedAliases(). $this->addDefaultParametersMethod() ; @@ -1115,6 +1116,15 @@ private function addMethodMap(): string } } + $aliases = $this->container->getAliases(); + foreach ($aliases as $alias => $id) { + if (!$id->isDeprecated()) { + continue; + } + $id = (string) $id; + $code .= ' '.$this->doExport($alias).' => '.$this->doExport($this->generateMethodName($alias)).",\n"; + } + return $code ? " \$this->methodMap = [\n{$code} ];\n" : ''; } @@ -1141,6 +1151,10 @@ private function addAliases(): string $code = " \$this->aliases = [\n"; ksort($aliases); foreach ($aliases as $alias => $id) { + if ($id->isDeprecated()) { + continue; + } + $id = (string) $id; while (isset($aliases[$id])) { $id = (string) $aliases[$id]; @@ -1151,6 +1165,39 @@ private function addAliases(): string return $code." ];\n"; } + private function addDeprecatedAliases(): string + { + $code = ''; + $aliases = $this->container->getAliases(); + foreach ($aliases as $alias => $definition) { + if (!$definition->isDeprecated()) { + continue; + } + $public = $definition->isPublic() ? 'public' : 'private'; + $id = (string) $definition; + $methodNameAlias = $this->generateMethodName($alias); + $idExported = $this->export($id); + $messageExported = $this->export($definition->getDeprecationMessage($alias)); + $code = <<docStar} + * Gets the $public '$alias' alias. + * + * @return object The "$id" service. + */ + protected function {$methodNameAlias}() + { + @trigger_error($messageExported, E_USER_DEPRECATED); + + return \$this->get($idExported); + } + +EOF; + } + + return $code; + } + private function addInlineRequires(): string { if (!$this->hotPathTag || !$this->inlineRequires) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 61b30a22e43ad..23e52d0e71d62 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -671,12 +671,11 @@ private function validateAlias(\DOMElement $alias, $file) } } - $allowedTags = array('deprecated'); foreach ($alias->childNodes as $child) { if (!$child instanceof \DOMElement && self::NS !== $child->namespaceURI) { continue; } - if (!in_array($child->localName, $allowedTags, true)) { + if (!\in_array($child->localName, ['deprecated'], true)) { throw new InvalidArgumentException(sprintf('Invalid child element "%s" defined for alias "%s" in "%s".', $child->localName, $alias->getAttribute('id'), $file)); } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 13d6746c10a59..f8ebfd7b6b11d 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -351,7 +351,6 @@ private function parseDefinition($id, $service, $file, array $defaults) foreach ($service as $key => $value) { if (!\in_array($key, ['alias', 'public', 'deprecated'])) { throw new InvalidArgumentException(sprintf('The configuration key "%s" is unsupported for the service "%s" which is defined as an alias in "%s". Allowed configuration keys for service aliases are "alias" and "public".', $key, $id, $file)); - continue; } if ('deprecated' === $key) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveReferencesToAliasesPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveReferencesToAliasesPassTest.php index 55b47057b1fd5..96a59639a5a26 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveReferencesToAliasesPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveReferencesToAliasesPassTest.php @@ -83,6 +83,48 @@ public function testResolveFactory() $this->assertSame('Factory', (string) $resolvedBarFactory[0]); } + /** + * @group legacy + * @expectedDeprecation The "deprecated_foo_alias" service alias is deprecated. You should stop using it, as it will soon be removed. + */ + public function testDeprecationNoticeWhenReferencedByAlias() + { + $container = new ContainerBuilder(); + + $container->register('foo', 'stdClass'); + + $aliasDeprecated = new Alias('foo'); + $aliasDeprecated->setDeprecated(true); + $container->setAlias('deprecated_foo_alias', $aliasDeprecated); + + $alias = new Alias('deprecated_foo_alias'); + $container->setAlias('alias', $alias); + + $this->process($container); + } + + /** + * @group legacy + * @expectedDeprecation The "foo_aliased" service alias is deprecated. You should stop using it, as it will soon be removed. + */ + public function testDeprecationNoticeWhenReferencedByDefinition() + { + $container = new ContainerBuilder(); + + $container->register('foo', 'stdClass'); + + $aliasDeprecated = new Alias('foo'); + $aliasDeprecated->setDeprecated(true); + $container->setAlias('foo_aliased', $aliasDeprecated); + + $container + ->register('definition') + ->setArguments([new Reference('foo_aliased')]) + ; + + $this->process($container); + } + protected function process(ContainerBuilder $container) { $pass = new ResolveReferencesToAliasesPass(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 6f1fb76719cce..214c619a1bbea 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -325,6 +325,24 @@ public function testAliases() $this->assertSame($foo, $container->get('alias_for_alias')); } + /** + * @group legacy + * @expectedDeprecation The "alias_for_foo_deprecated" service alias is deprecated. You should stop using it, as it will soon be removed. + */ + public function testAliasesDeprecation() + { + $container = include self::$fixturesPath.'/containers/container_alias_deprecation.php'; + $container->compile(); + $dumper = new PhpDumper($container); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/container_alias_deprecation.php', $dumper->dump(['class' => 'Symfony_DI_PhpDumper_Test_Aliases_Deprecation'])); + + require self::$fixturesPath.'/php/container_alias_deprecation.php'; + $container = new \Symfony_DI_PhpDumper_Test_Aliases_Deprecation(); + $container->get('alias_for_foo_non_deprecated'); + $container->get('alias_for_foo_deprecated'); + } + public function testFrozenContainerWithoutAliases() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_alias_deprecation.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_alias_deprecation.php new file mode 100644 index 0000000000000..b9369172a2b43 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_alias_deprecation.php @@ -0,0 +1,21 @@ +register('foo', 'stdClass') + ->setPublic(true) +; + +$container + ->setAlias('alias_for_foo_deprecated', 'foo') + ->setDeprecated(true) + ->setPublic(true); + +$container + ->setAlias('alias_for_foo_non_deprecated', 'foo') + ->setPublic(true); + +return $container; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_alias_deprecation.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_alias_deprecation.php new file mode 100644 index 0000000000000..c7f6c5432ffea --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_alias_deprecation.php @@ -0,0 +1,73 @@ +services = $this->privates = []; + $this->methodMap = [ + 'foo' => 'getFooService', + 'alias_for_foo_deprecated' => 'getAliasForFooDeprecatedService', + ]; + $this->aliases = [ + 'alias_for_foo_non_deprecated' => 'foo', + ]; + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return [ + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ]; + } + + /** + * Gets the public 'foo' shared service. + * + * @return \stdClass + */ + protected function getFooService() + { + return $this->services['foo'] = new \stdClass(); + } + + /** + * Gets the public 'alias_for_foo_deprecated' alias. + * + * @return object The "foo" service. + */ + protected function getAliasForFooDeprecatedService() + { + @trigger_error('The "alias_for_foo_deprecated" service alias is deprecated. You should stop using it, as it will soon be removed.', E_USER_DEPRECATED); + + return $this->get('foo'); + } +}