diff --git a/src/Symfony/Component/DependencyInjection/Alias.php b/src/Symfony/Component/DependencyInjection/Alias.php index 24484de167e87..e61084c6f0b59 100644 --- a/src/Symfony/Component/DependencyInjection/Alias.php +++ b/src/Symfony/Component/DependencyInjection/Alias.php @@ -11,17 +11,24 @@ namespace Symfony\Component\DependencyInjection; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + class Alias { private $id; private $public; private $private; + private $deprecated; + private $deprecationTemplate; + + private static $defaultDeprecationTemplate = 'The "%service_id%" service alias is deprecated. You should stop using it, as it will soon be removed.'; public function __construct(string $id, bool $public = true) { $this->id = $id; $this->public = $public; $this->private = 2 > \func_num_args(); + $this->deprecated = false; } /** @@ -78,6 +85,46 @@ public function isPrivate() return $this->private; } + /** + * Whether this alias is deprecated, that means it should not be referenced + * anymore. + * + * @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 + * + * @throws InvalidArgumentException when the message template is invalid + */ + public function setDeprecated($status = true, $template = null) + { + if (null !== $template) { + if (preg_match('#[\r\n]|\*/#', $template)) { + throw new InvalidArgumentException('Invalid characters found in deprecation template.'); + } + + if (false === strpos($template, '%service_id%')) { + throw new InvalidArgumentException('The deprecation template must contain the "%service_id%" placeholder.'); + } + + $this->deprecationTemplate = $template; + } + + $this->deprecated = (bool) $status; + + return $this; + } + + public function isDeprecated(): bool + { + return $this->deprecated; + } + + public function getDeprecationMessage(string $id): string + { + return str_replace('%service_id%', $id, $this->deprecationTemplate ?: self::$defaultDeprecationTemplate); + } + /** * Returns the Id of this alias. * 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 58a4bfc48422f..25cef99de875a 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -579,7 +579,13 @@ private function doGet($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_ } if (!isset($this->definitions[$id]) && isset($this->aliasDefinitions[$id])) { - return $this->doGet((string) $this->aliasDefinitions[$id], $invalidBehavior, $inlineServices, $isConstructorArgument); + $alias = $this->aliasDefinitions[$id]; + + if ($alias->isDeprecated()) { + @trigger_error($alias->getDeprecationMessage($id), E_USER_DEPRECATED); + } + + 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 c5292ecf688bf..23e52d0e71d62 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -220,6 +220,10 @@ private function parseDefinition(\DOMElement $service, $file, array $defaults) $alias->setPublic($defaults['public']); } + if ($deprecated = $this->getChildren($service, 'deprecated')) { + $alias->setDeprecated(true, $deprecated[0]->nodeValue ?: null); + } + return; } @@ -668,7 +672,10 @@ private function validateAlias(\DOMElement $alias, $file) } foreach ($alias->childNodes as $child) { - if ($child instanceof \DOMElement && self::NS === $child->namespaceURI) { + if (!$child instanceof \DOMElement && self::NS !== $child->namespaceURI) { + continue; + } + 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 05fbe86363a93..f8ebfd7b6b11d 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -349,9 +349,13 @@ private function parseDefinition($id, $service, $file, array $defaults) } foreach ($service as $key => $value) { - if (!\in_array($key, ['alias', 'public'])) { + 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)); } + + if ('deprecated' === $key) { + $alias->setDeprecated(true, $value); + } } return; diff --git a/src/Symfony/Component/DependencyInjection/Tests/AliasTest.php b/src/Symfony/Component/DependencyInjection/Tests/AliasTest.php new file mode 100644 index 0000000000000..44144b240b51c --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/AliasTest.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Alias; + +class AliasTest extends TestCase +{ + public function testConstructor() + { + $alias = new Alias('foo'); + + $this->assertEquals('foo', (string) $alias); + $this->assertTrue($alias->isPublic()); + } + + public function testCanConstructANonPublicAlias() + { + $alias = new Alias('foo', false); + + $this->assertEquals('foo', (string) $alias); + $this->assertFalse($alias->isPublic()); + } + + public function testCanConstructAPrivateAlias() + { + $alias = new Alias('foo', false, false); + + $this->assertEquals('foo', (string) $alias); + $this->assertFalse($alias->isPublic()); + $this->assertFalse($alias->isPrivate()); + } + + public function testCanSetPublic() + { + $alias = new Alias('foo', false); + $alias->setPublic(true); + + $this->assertTrue($alias->isPublic()); + } + + public function testCanDeprecateAnAlias() + { + $alias = new Alias('foo', false); + $alias->setDeprecated(true, 'The %service_id% service is deprecated.'); + + $this->assertTrue($alias->isDeprecated()); + } + + public function testItHasADefaultDeprecationMessage() + { + $alias = new Alias('foo', false); + $alias->setDeprecated(); + + $expectedMessage = 'The "foo" service alias is deprecated. You should stop using it, as it will soon be removed.'; + $this->assertEquals($expectedMessage, $alias->getDeprecationMessage('foo')); + } + + public function testReturnsCorrectDeprecationMessage() + { + $alias = new Alias('foo', false); + $alias->setDeprecated(true, 'The "%service_id%" is deprecated.'); + + $expectedMessage = 'The "foo" is deprecated.'; + $this->assertEquals($expectedMessage, $alias->getDeprecationMessage('foo')); + } + + public function testCanOverrideDeprecation() + { + $alias = new Alias('foo', false); + $alias->setDeprecated(); + + $initial = $alias->isDeprecated(); + $alias->setDeprecated(false); + $final = $alias->isDeprecated(); + + $this->assertTrue($initial); + $this->assertFalse($final); + } + + /** + * @dataProvider invalidDeprecationMessageProvider + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + */ + public function testCannotDeprecateWithAnInvalidTemplate($message) + { + $def = new Alias('foo'); + $def->setDeprecated(true, $message); + } + + public function invalidDeprecationMessageProvider() + { + return [ + "With \rs" => ["invalid \r message %service_id%"], + "With \ns" => ["invalid \n message %service_id%"], + 'With */s' => ['invalid */ message %service_id%'], + 'message not containing required %service_id% variable' => ['this is deprecated'], + ]; + } +} 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/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 83c0458e5c671..cec7ea4d183de 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -259,6 +259,22 @@ public function testAliases() } } + /** + * @group legacy + * @expectedDeprecation The "foobar" service alias is deprecated. You should stop using it, as it will soon be removed. + */ + public function testDeprecatedAlias() + { + $builder = new ContainerBuilder(); + $builder->register('foo', 'stdClass'); + + $alias = new Alias('foo'); + $alias->setDeprecated(); + $builder->setAlias('foobar', $alias); + + $builder->get('foobar'); + } + public function testGetAliases() { $builder = new ContainerBuilder(); 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'); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/deprecated_alias_definitions.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/deprecated_alias_definitions.xml new file mode 100644 index 0000000000000..83ceeefa9c163 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/deprecated_alias_definitions.xml @@ -0,0 +1,13 @@ + + + + + + + + + + The "%service_id%" service alias is deprecated. + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/deprecated_alias_definitions.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/deprecated_alias_definitions.yml new file mode 100644 index 0000000000000..2223597815f6d --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/deprecated_alias_definitions.yml @@ -0,0 +1,4 @@ +services: + alias_for_foobar: + alias: foobar + deprecated: The "%service_id%" service alias is deprecated. diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index 6d9634c66cba8..e5037d809b593 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -351,6 +351,21 @@ public function testDeprecated() $this->assertSame($message, $container->getDefinition('bar')->getDeprecationMessage('bar')); } + public function testDeprecatedAliases() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('deprecated_alias_definitions.xml'); + + $this->assertTrue($container->getAlias('alias_for_foo')->isDeprecated()); + $message = 'The "alias_for_foo" service alias is deprecated. You should stop using it, as it will soon be removed.'; + $this->assertSame($message, $container->getAlias('alias_for_foo')->getDeprecationMessage('alias_for_foo')); + + $this->assertTrue($container->getAlias('alias_for_foobar')->isDeprecated()); + $message = 'The "alias_for_foobar" service alias is deprecated.'; + $this->assertSame($message, $container->getAlias('alias_for_foobar')->getDeprecationMessage('alias_for_foobar')); + } + public function testConvertDomElementToArray() { $doc = new \DOMDocument('1.0'); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index 37e6054f5545f..8c9ccaf06ff93 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -175,6 +175,17 @@ public function testLoadServices() $this->assertEquals(['decorated', 'decorated.pif-pouf', 5], $services['decorator_service_with_name_and_priority']->getDecoratedService()); } + public function testDeprecatedAliases() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('deprecated_alias_definitions.yml'); + + $this->assertTrue($container->getAlias('alias_for_foobar')->isDeprecated()); + $message = 'The "alias_for_foobar" service alias is deprecated.'; + $this->assertSame($message, $container->getAlias('alias_for_foobar')->getDeprecationMessage('alias_for_foobar')); + } + public function testLoadFactoryShortSyntax() { $container = new ContainerBuilder(); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy