Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit edc4a0f

Browse files
feature #29968 [DI] Added support for deprecating aliases (j92, Renan)
This PR was merged into the 4.3-dev branch. Discussion ---------- [DI] Added support for deprecating aliases | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? |no | Tests pass? | yes | Fixed tickets | #24507 | License | MIT | Doc PR | TBD This PR is a continuity of #24707 Commits ------- 6c571ad Added support for deprecating aliases (runtime+dumper) 0eb071b Added support for deprecating an alias
2 parents 0901bbe + 6c571ad commit edc4a0f

16 files changed

+446
-4
lines changed

src/Symfony/Component/DependencyInjection/Alias.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,24 @@
1111

1212
namespace Symfony\Component\DependencyInjection;
1313

14+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
15+
1416
class Alias
1517
{
1618
private $id;
1719
private $public;
1820
private $private;
21+
private $deprecated;
22+
private $deprecationTemplate;
23+
24+
private static $defaultDeprecationTemplate = 'The "%service_id%" service alias is deprecated. You should stop using it, as it will soon be removed.';
1925

2026
public function __construct(string $id, bool $public = true)
2127
{
2228
$this->id = $id;
2329
$this->public = $public;
2430
$this->private = 2 > \func_num_args();
31+
$this->deprecated = false;
2532
}
2633

2734
/**
@@ -78,6 +85,46 @@ public function isPrivate()
7885
return $this->private;
7986
}
8087

88+
/**
89+
* Whether this alias is deprecated, that means it should not be referenced
90+
* anymore.
91+
*
92+
* @param bool $status Whether this alias is deprecated, defaults to true
93+
* @param string $template Optional template message to use if the alias is deprecated
94+
*
95+
* @return $this
96+
*
97+
* @throws InvalidArgumentException when the message template is invalid
98+
*/
99+
public function setDeprecated($status = true, $template = null)
100+
{
101+
if (null !== $template) {
102+
if (preg_match('#[\r\n]|\*/#', $template)) {
103+
throw new InvalidArgumentException('Invalid characters found in deprecation template.');
104+
}
105+
106+
if (false === strpos($template, '%service_id%')) {
107+
throw new InvalidArgumentException('The deprecation template must contain the "%service_id%" placeholder.');
108+
}
109+
110+
$this->deprecationTemplate = $template;
111+
}
112+
113+
$this->deprecated = (bool) $status;
114+
115+
return $this;
116+
}
117+
118+
public function isDeprecated(): bool
119+
{
120+
return $this->deprecated;
121+
}
122+
123+
public function getDeprecationMessage(string $id): string
124+
{
125+
return str_replace('%service_id%', $id, $this->deprecationTemplate ?: self::$defaultDeprecationTemplate);
126+
}
127+
81128
/**
82129
* Returns the Id of this alias.
83130
*

src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public function process(ContainerBuilder $container)
3131

3232
foreach ($container->getAliases() as $id => $alias) {
3333
$aliasId = (string) $alias;
34+
3435
if ($aliasId !== $defId = $this->getDefinitionId($aliasId, $container)) {
3536
$container->setAlias($id, $defId)->setPublic($alias->isPublic())->setPrivate($alias->isPrivate());
3637
}
@@ -60,8 +61,15 @@ private function getDefinitionId(string $id, ContainerBuilder $container): strin
6061
if (isset($seen[$id])) {
6162
throw new ServiceCircularReferenceException($id, array_merge(array_keys($seen), [$id]));
6263
}
64+
6365
$seen[$id] = true;
64-
$id = (string) $container->getAlias($id);
66+
$alias = $container->getAlias($id);
67+
68+
if ($alias->isDeprecated()) {
69+
@trigger_error($alias->getDeprecationMessage($id), E_USER_DEPRECATED);
70+
}
71+
72+
$id = (string) $alias;
6573
}
6674

6775
return $id;

src/Symfony/Component/DependencyInjection/ContainerBuilder.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,13 @@ private function doGet($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_
579579
}
580580

581581
if (!isset($this->definitions[$id]) && isset($this->aliasDefinitions[$id])) {
582-
return $this->doGet((string) $this->aliasDefinitions[$id], $invalidBehavior, $inlineServices, $isConstructorArgument);
582+
$alias = $this->aliasDefinitions[$id];
583+
584+
if ($alias->isDeprecated()) {
585+
@trigger_error($alias->getDeprecationMessage($id), E_USER_DEPRECATED);
586+
}
587+
588+
return $this->doGet((string) $alias, $invalidBehavior, $inlineServices, $isConstructorArgument);
583589
}
584590

585591
try {

src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ public function dump(array $options = [])
218218
$code =
219219
$this->startClass($options['class'], $baseClass, $baseClassWithNamespace).
220220
$this->addServices($services).
221+
$this->addDeprecatedAliases().
221222
$this->addDefaultParametersMethod()
222223
;
223224

@@ -1115,6 +1116,15 @@ private function addMethodMap(): string
11151116
}
11161117
}
11171118

1119+
$aliases = $this->container->getAliases();
1120+
foreach ($aliases as $alias => $id) {
1121+
if (!$id->isDeprecated()) {
1122+
continue;
1123+
}
1124+
$id = (string) $id;
1125+
$code .= ' '.$this->doExport($alias).' => '.$this->doExport($this->generateMethodName($alias)).",\n";
1126+
}
1127+
11181128
return $code ? " \$this->methodMap = [\n{$code} ];\n" : '';
11191129
}
11201130

@@ -1141,6 +1151,10 @@ private function addAliases(): string
11411151
$code = " \$this->aliases = [\n";
11421152
ksort($aliases);
11431153
foreach ($aliases as $alias => $id) {
1154+
if ($id->isDeprecated()) {
1155+
continue;
1156+
}
1157+
11441158
$id = (string) $id;
11451159
while (isset($aliases[$id])) {
11461160
$id = (string) $aliases[$id];
@@ -1151,6 +1165,39 @@ private function addAliases(): string
11511165
return $code." ];\n";
11521166
}
11531167

1168+
private function addDeprecatedAliases(): string
1169+
{
1170+
$code = '';
1171+
$aliases = $this->container->getAliases();
1172+
foreach ($aliases as $alias => $definition) {
1173+
if (!$definition->isDeprecated()) {
1174+
continue;
1175+
}
1176+
$public = $definition->isPublic() ? 'public' : 'private';
1177+
$id = (string) $definition;
1178+
$methodNameAlias = $this->generateMethodName($alias);
1179+
$idExported = $this->export($id);
1180+
$messageExported = $this->export($definition->getDeprecationMessage($alias));
1181+
$code = <<<EOF
1182+
1183+
/*{$this->docStar}
1184+
* Gets the $public '$alias' alias.
1185+
*
1186+
* @return object The "$id" service.
1187+
*/
1188+
protected function {$methodNameAlias}()
1189+
{
1190+
@trigger_error($messageExported, E_USER_DEPRECATED);
1191+
1192+
return \$this->get($idExported);
1193+
}
1194+
1195+
EOF;
1196+
}
1197+
1198+
return $code;
1199+
}
1200+
11541201
private function addInlineRequires(): string
11551202
{
11561203
if (!$this->hotPathTag || !$this->inlineRequires) {

src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,10 @@ private function parseDefinition(\DOMElement $service, $file, array $defaults)
220220
$alias->setPublic($defaults['public']);
221221
}
222222

223+
if ($deprecated = $this->getChildren($service, 'deprecated')) {
224+
$alias->setDeprecated(true, $deprecated[0]->nodeValue ?: null);
225+
}
226+
223227
return;
224228
}
225229

@@ -668,7 +672,10 @@ private function validateAlias(\DOMElement $alias, $file)
668672
}
669673

670674
foreach ($alias->childNodes as $child) {
671-
if ($child instanceof \DOMElement && self::NS === $child->namespaceURI) {
675+
if (!$child instanceof \DOMElement && self::NS !== $child->namespaceURI) {
676+
continue;
677+
}
678+
if (!\in_array($child->localName, ['deprecated'], true)) {
672679
throw new InvalidArgumentException(sprintf('Invalid child element "%s" defined for alias "%s" in "%s".', $child->localName, $alias->getAttribute('id'), $file));
673680
}
674681
}

src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,9 +349,13 @@ private function parseDefinition($id, $service, $file, array $defaults)
349349
}
350350

351351
foreach ($service as $key => $value) {
352-
if (!\in_array($key, ['alias', 'public'])) {
352+
if (!\in_array($key, ['alias', 'public', 'deprecated'])) {
353353
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));
354354
}
355+
356+
if ('deprecated' === $key) {
357+
$alias->setDeprecated(true, $value);
358+
}
355359
}
356360

357361
return;
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Tests;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\DependencyInjection\Alias;
16+
17+
class AliasTest extends TestCase
18+
{
19+
public function testConstructor()
20+
{
21+
$alias = new Alias('foo');
22+
23+
$this->assertEquals('foo', (string) $alias);
24+
$this->assertTrue($alias->isPublic());
25+
}
26+
27+
public function testCanConstructANonPublicAlias()
28+
{
29+
$alias = new Alias('foo', false);
30+
31+
$this->assertEquals('foo', (string) $alias);
32+
$this->assertFalse($alias->isPublic());
33+
}
34+
35+
public function testCanConstructAPrivateAlias()
36+
{
37+
$alias = new Alias('foo', false, false);
38+
39+
$this->assertEquals('foo', (string) $alias);
40+
$this->assertFalse($alias->isPublic());
41+
$this->assertFalse($alias->isPrivate());
42+
}
43+
44+
public function testCanSetPublic()
45+
{
46+
$alias = new Alias('foo', false);
47+
$alias->setPublic(true);
48+
49+
$this->assertTrue($alias->isPublic());
50+
}
51+
52+
public function testCanDeprecateAnAlias()
53+
{
54+
$alias = new Alias('foo', false);
55+
$alias->setDeprecated(true, 'The %service_id% service is deprecated.');
56+
57+
$this->assertTrue($alias->isDeprecated());
58+
}
59+
60+
public function testItHasADefaultDeprecationMessage()
61+
{
62+
$alias = new Alias('foo', false);
63+
$alias->setDeprecated();
64+
65+
$expectedMessage = 'The "foo" service alias is deprecated. You should stop using it, as it will soon be removed.';
66+
$this->assertEquals($expectedMessage, $alias->getDeprecationMessage('foo'));
67+
}
68+
69+
public function testReturnsCorrectDeprecationMessage()
70+
{
71+
$alias = new Alias('foo', false);
72+
$alias->setDeprecated(true, 'The "%service_id%" is deprecated.');
73+
74+
$expectedMessage = 'The "foo" is deprecated.';
75+
$this->assertEquals($expectedMessage, $alias->getDeprecationMessage('foo'));
76+
}
77+
78+
public function testCanOverrideDeprecation()
79+
{
80+
$alias = new Alias('foo', false);
81+
$alias->setDeprecated();
82+
83+
$initial = $alias->isDeprecated();
84+
$alias->setDeprecated(false);
85+
$final = $alias->isDeprecated();
86+
87+
$this->assertTrue($initial);
88+
$this->assertFalse($final);
89+
}
90+
91+
/**
92+
* @dataProvider invalidDeprecationMessageProvider
93+
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
94+
*/
95+
public function testCannotDeprecateWithAnInvalidTemplate($message)
96+
{
97+
$def = new Alias('foo');
98+
$def->setDeprecated(true, $message);
99+
}
100+
101+
public function invalidDeprecationMessageProvider()
102+
{
103+
return [
104+
"With \rs" => ["invalid \r message %service_id%"],
105+
"With \ns" => ["invalid \n message %service_id%"],
106+
'With */s' => ['invalid */ message %service_id%'],
107+
'message not containing required %service_id% variable' => ['this is deprecated'],
108+
];
109+
}
110+
}

src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveReferencesToAliasesPassTest.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,48 @@ public function testResolveFactory()
8383
$this->assertSame('Factory', (string) $resolvedBarFactory[0]);
8484
}
8585

86+
/**
87+
* @group legacy
88+
* @expectedDeprecation The "deprecated_foo_alias" service alias is deprecated. You should stop using it, as it will soon be removed.
89+
*/
90+
public function testDeprecationNoticeWhenReferencedByAlias()
91+
{
92+
$container = new ContainerBuilder();
93+
94+
$container->register('foo', 'stdClass');
95+
96+
$aliasDeprecated = new Alias('foo');
97+
$aliasDeprecated->setDeprecated(true);
98+
$container->setAlias('deprecated_foo_alias', $aliasDeprecated);
99+
100+
$alias = new Alias('deprecated_foo_alias');
101+
$container->setAlias('alias', $alias);
102+
103+
$this->process($container);
104+
}
105+
106+
/**
107+
* @group legacy
108+
* @expectedDeprecation The "foo_aliased" service alias is deprecated. You should stop using it, as it will soon be removed.
109+
*/
110+
public function testDeprecationNoticeWhenReferencedByDefinition()
111+
{
112+
$container = new ContainerBuilder();
113+
114+
$container->register('foo', 'stdClass');
115+
116+
$aliasDeprecated = new Alias('foo');
117+
$aliasDeprecated->setDeprecated(true);
118+
$container->setAlias('foo_aliased', $aliasDeprecated);
119+
120+
$container
121+
->register('definition')
122+
->setArguments([new Reference('foo_aliased')])
123+
;
124+
125+
$this->process($container);
126+
}
127+
86128
protected function process(ContainerBuilder $container)
87129
{
88130
$pass = new ResolveReferencesToAliasesPass();

0 commit comments

Comments
 (0)