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 0df92cb

Browse filesBrowse files
committed
[DependencyInjection] Add a mechanism to deprecate public services to private
1 parent 0633308 commit 0df92cb
Copy full SHA for 0df92cb

File tree

7 files changed

+236
-0
lines changed
Filter options

7 files changed

+236
-0
lines changed

‎src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class UnusedTagsPass implements CompilerPassInterface
3434
'container.hot_path',
3535
'container.no_preload',
3636
'container.preload',
37+
'container.private',
3738
'container.reversible',
3839
'container.service_locator',
3940
'container.service_locator_context',

‎src/Symfony/Component/DependencyInjection/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ CHANGELOG
1919
* deprecated `Definition::getDeprecationMessage()`, use `Definition::getDeprecation()` instead
2020
* deprecated `Alias::getDeprecationMessage()`, use `Alias::getDeprecation()` instead
2121
* deprecated PHP-DSL's `inline()` function, use `service()` instead
22+
* added `AliasDeprecatedPublicServicesPass` to deprecate public services to private
2223

2324
5.0.0
2425
-----
+73Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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\Compiler;
13+
14+
use Symfony\Component\DependencyInjection\ContainerBuilder;
15+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
16+
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
17+
use Symfony\Component\DependencyInjection\Reference;
18+
19+
final class AliasDeprecatedPublicServicesPass extends AbstractRecursivePass
20+
{
21+
private $tagName;
22+
23+
private $aliases = [];
24+
25+
public function __construct(string $tagName = 'container.private')
26+
{
27+
$this->tagName = $tagName;
28+
}
29+
30+
/**
31+
* {@inheritdoc}
32+
*/
33+
protected function processValue($value, bool $isRoot = false)
34+
{
35+
if ($value instanceof Reference && isset($this->aliases[$id = (string) $value])) {
36+
return new Reference($this->aliases[$id]);
37+
}
38+
39+
return parent::processValue($value, $isRoot);
40+
}
41+
42+
/**
43+
* {@inheritdoc}
44+
*/
45+
public function process(ContainerBuilder $container)
46+
{
47+
foreach ($container->findTaggedServiceIds($this->tagName) as $id => $tags) {
48+
if (null === $package = $tags[0]['package'] ?? null) {
49+
throw new InvalidArgumentException(sprintf('The "package" attribute is mandatory for the "%s" tag on the "%s" service.', $this->tagName, $id));
50+
}
51+
52+
if (null === $version = $tags[0]['version'] ?? null) {
53+
throw new InvalidArgumentException(sprintf('The "version" attribute is mandatory for the "%s" tag on the "%s" service.', $this->tagName, $id));
54+
}
55+
56+
$definition = $container->getDefinition($id);
57+
if (!$definition->isPublic() || $definition->isPrivate()) {
58+
throw new RuntimeException(sprintf('The "%s" service is not public. Only public services can be deprecated with the "%s" tag.', $id, $this->tagName));
59+
}
60+
61+
$container
62+
->setAlias($id, $aliasId = '.'.$this->tagName.'.'.ContainerBuilder::hash($id))
63+
->setPublic(true)
64+
->setDeprecated($package, $version, 'The public "%alias_id%" service will be private in the future. You should not access it directly from the container at runtime.');
65+
66+
$container->setDefinition($aliasId, $definition);
67+
68+
$this->aliases[$id] = $aliasId;
69+
}
70+
71+
parent::process($container);
72+
}
73+
}

‎src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ public function __construct()
9494
new CheckExceptionOnInvalidReferenceBehaviorPass(),
9595
new ResolveHotPathPass(),
9696
new ResolveNoPreloadPass(),
97+
new AliasDeprecatedPublicServicesPass(),
9798
]];
9899
}
99100

+74Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use Symfony\Component\DependencyInjection\Compiler\AliasDeprecatedPublicServicesPass;
7+
use Symfony\Component\DependencyInjection\ContainerBuilder;
8+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
9+
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
10+
11+
final class AliasDeprecatedPublicServicesPassTest extends TestCase
12+
{
13+
public function testProcess()
14+
{
15+
$container = new ContainerBuilder();
16+
$container
17+
->register('foo')
18+
->setPublic(true)
19+
->addTag('container.private', ['package' => 'foo/bar', 'version' => '1.2']);
20+
21+
(new AliasDeprecatedPublicServicesPass())->process($container);
22+
23+
$this->assertTrue($container->hasAlias('foo'));
24+
25+
$alias = $container->getAlias('foo');
26+
27+
$this->assertSame('.container.private.EVZD5I4', (string) $alias);
28+
$this->assertTrue($alias->isPublic());
29+
$this->assertFalse($alias->isPrivate());
30+
$this->assertSame([
31+
'package' => 'foo/bar',
32+
'version' => '1.2',
33+
'message' => 'The public "foo" service will be private in the future. You should not access it directly from the container at runtime.',
34+
], $alias->getDeprecation('foo'));
35+
}
36+
37+
/**
38+
* @dataProvider processWithMissingAttributeProvider
39+
*/
40+
public function testProcessWithMissingAttribute(string $attribute, array $attributes)
41+
{
42+
$this->expectException(InvalidArgumentException::class);
43+
$this->expectExceptionMessage(sprintf('The "%s" attribute is mandatory for the "container.private" tag on the "foo" service.', $attribute));
44+
45+
$container = new ContainerBuilder();
46+
$container
47+
->register('foo')
48+
->addTag('container.private', $attributes);
49+
50+
(new AliasDeprecatedPublicServicesPass())->process($container);
51+
}
52+
53+
public function processWithMissingAttributeProvider()
54+
{
55+
return [
56+
['package', ['version' => '1.2']],
57+
['version', ['package' => 'foo/bar']],
58+
];
59+
}
60+
61+
public function testProcessWithNonPublicService()
62+
{
63+
$this->expectException(RuntimeException::class);
64+
$this->expectExceptionMessage('The "foo" service is not public. Only public services can be deprecated with the "container.private" tag.');
65+
66+
$container = new ContainerBuilder();
67+
$container
68+
->register('foo')
69+
->setPublic(false)
70+
->addTag('container.private', ['package' => 'foo/bar', 'version' => '1.2']);
71+
72+
(new AliasDeprecatedPublicServicesPass())->process($container);
73+
}
74+
}

‎src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
+38Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1641,6 +1641,44 @@ public function testAutoAliasing()
16411641

16421642
$this->assertInstanceOf(D::class, $container->get(X::class));
16431643
}
1644+
1645+
/**
1646+
* @group legacy
1647+
*/
1648+
public function testDirectlyAccessingDeprecatedPublicService()
1649+
{
1650+
$this->expectDeprecation('Since foo/bar 3.8: The public "Symfony\Component\DependencyInjection\Tests\A" service will be private in the future. You should not access it directly from the container at runtime.');
1651+
1652+
$container = new ContainerBuilder();
1653+
$container
1654+
->register(A::class)
1655+
->setPublic(true)
1656+
->addTag('container.private', ['package' => 'foo/bar', 'version' => '3.8']);
1657+
1658+
$container->compile();
1659+
1660+
$container->get(A::class);
1661+
}
1662+
1663+
public function testReferencingDeprecatedPublicService()
1664+
{
1665+
$container = new ContainerBuilder();
1666+
$container
1667+
->register(A::class)
1668+
->setPublic(true)
1669+
->addTag('container.private', ['package' => 'foo/bar', 'version' => '3.8']);
1670+
$container
1671+
->register(B::class)
1672+
->setPublic(true)
1673+
->addArgument(new Reference(A::class));
1674+
1675+
$container->compile();
1676+
1677+
// No deprecation should be triggered.
1678+
$container->get(B::class);
1679+
1680+
$this->addToAssertionCount(1);
1681+
}
16441682
}
16451683

16461684
class FooClass

‎src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
+48Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,6 +1403,54 @@ public function testDumpServiceWithAbstractArgument()
14031403
$dumper = new PhpDumper($container);
14041404
$dumper->dump();
14051405
}
1406+
1407+
/**
1408+
* @group legacy
1409+
*/
1410+
public function testDirectlyAccessingDeprecatedPublicService()
1411+
{
1412+
$this->expectDeprecation('Since foo/bar 3.8: The public "bar" service will be private in the future. You should not access it directly from the container at runtime.');
1413+
1414+
$container = new ContainerBuilder();
1415+
$container
1416+
->register('bar', \BarClass::class)
1417+
->setPublic(true)
1418+
->addTag('container.private', ['package' => 'foo/bar', 'version' => '3.8']);
1419+
1420+
$container->compile();
1421+
1422+
$dumper = new PhpDumper($container);
1423+
eval('?>'.$dumper->dump(['class' => 'Symfony_DI_PhpDumper_Test_Directly_Accessing_Deprecated_Public_Service']));
1424+
1425+
$container = new \Symfony_DI_PhpDumper_Test_Directly_Accessing_Deprecated_Public_Service();
1426+
1427+
$container->get('bar');
1428+
}
1429+
1430+
public function testReferencingDeprecatedPublicService()
1431+
{
1432+
$container = new ContainerBuilder();
1433+
$container
1434+
->register('bar', \BarClass::class)
1435+
->setPublic(true)
1436+
->addTag('container.private', ['package' => 'foo/bar', 'version' => '3.8']);
1437+
$container
1438+
->register('bar_user', \BarUserClass::class)
1439+
->setPublic(true)
1440+
->addArgument(new Reference('bar'));
1441+
1442+
$container->compile();
1443+
1444+
$dumper = new PhpDumper($container);
1445+
eval('?>'.$dumper->dump(['class' => 'Symfony_DI_PhpDumper_Test_Referencing_Deprecated_Public_Service']));
1446+
1447+
$container = new \Symfony_DI_PhpDumper_Test_Referencing_Deprecated_Public_Service();
1448+
1449+
// No deprecation should be triggered.
1450+
$container->get('bar_user');
1451+
1452+
$this->addToAssertionCount(1);
1453+
}
14061454
}
14071455

14081456
class Rot13EnvVarProcessor implements EnvVarProcessorInterface

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.