Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit edc4a0f

Browse filesBrowse 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
Copy full SHA for edc4a0f
Expand file treeCollapse file tree

16 files changed

+446
-4
lines changed

‎src/Symfony/Component/DependencyInjection/Alias.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Alias.php
+47Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php
+9-1Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/ContainerBuilder.php
+7-1Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
+47Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
+8-1Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
+5-1Lines 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;
+110Lines 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 <fabien@symfony.com>
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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveReferencesToAliasesPassTest.php
+42Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.