8000 feature #48990 [DependencyInjection] deprecate the ``@required`` anno… · symfony/symfony@c8920e9 · GitHub
[go: up one dir, main page]

Skip to content

Commit c8920e9

Browse files
feature #48990 [DependencyInjection] deprecate the @required annotation (alexislefebvre)
This PR was squashed before being merged into the 6.3 branch. Discussion ---------- [DependencyInjection] deprecate the ``@required`` annotation | Q | A | ------------- | --- | Branch? | 6.3 | Bug fix? | no | New feature? | no | Deprecations? | yes | Tickets | Fix #48909 | License | MIT | Doc PR | - TODO: - [x] check the name of the package which should be reported in the deprecation message: - `symfony/framework-bundle` (that call `addGlobalIgnoredName('required')`)? - and/or - `symfony/dependency-injection` (that detect and is called when there is this annotation)? - [x] fix or remove the test of the deprecation? ` $this->expectDeprecation` doesn't work as I expected Commits ------- 9d69e4b [DependencyInjection] deprecate the ``@required`` annotation
2 parents 5c99187 + 9d69e4b commit c8920e9

14 files changed

+472
-35
lines changed

src/Symfony/Component/DependencyInjection/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ CHANGELOG
1717
* Add `#[Exclude]` to skip autoregistering a class
1818
* Add support for autowiring services as closures using `#[AutowireCallable]` or `#[AutowireServiceClosure]`
1919
* Deprecate `#[MapDecorated]`, use `#[AutowireDecorated]` instead
20+
* Deprecate the `@required` annotation, use the `Symfony\Contracts\Service\Attribute\Required` attribute instead
2021

2122
6.2
2223
---

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
use Symfony\Contracts\Service\Attribute\Required;
1616

1717
/**
18-
* Looks for definitions with autowiring enabled and registers their corresponding "@required" methods as setters.
18+
* Looks for definitions with autowiring enabled and registers their corresponding "#[Required]" methods as setters.
1919
*
2020
* @author Nicolas Grekas <p@tchwork.com>
2121
*/
@@ -57,6 +57,8 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed
5757
}
5858
if (false !== $doc = $r->getDocComment()) {
5959
if (false !== stripos($doc, '@required') && preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) {
60+
trigger_deprecation('symfony/dependency-injection', '6.3', 'Relying on the "@required" annotation on method "%s::%s()" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.', $reflectionMethod->class, $reflectionMethod->name);
61+
6062
if ($this->isWither($reflectionMethod, $doc)) {
6163
$withers[] = [$reflectionMethod->name, [], true];
6264
} else {

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
use Symfony\Contracts\Service\Attribute\Required;
1818

1919
/**
20-
* Looks for definitions with autowiring enabled and registers their corresponding "@required" properties.
20+
* Looks for definitions with autowiring enabled and registers their corresponding "#[Required]" properties.
2121
*
2222
* @author Sebastien Morel (Plopix) <morel.seb@gmail.com>
2323
* @author Nicolas Grekas <p@tchwork.com>
@@ -40,11 +40,15 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed
4040
if (!($type = $reflectionProperty->getType()) instanceof \ReflectionNamedType) {
4141
continue;
4242
}
43+
$doc = false;
4344
if (!$reflectionProperty->getAttributes(Required::class)
4445
&& ((false === $doc = $reflectionProperty->getDocComment()) || false === stripos($doc, '@required') || !preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc))
4546
) {
4647
continue;
4748
}
49+
if ($doc) {
50+
trigger_deprecation('symfony/dependency-injection', '6.3', 'Using the "@required" annotation on property "%s::$%s" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.', $reflectionProperty->class, $reflectionProperty->name);
51+
}
4852
if (\array_key_exists($name = $reflectionProperty->getName(), $properties)) {
4953
continue;
5054
}

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

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Psr\Log\LoggerInterface;
1616
use Psr\Log\NullLogger;
1717
use Symfony\Bridge\PhpUnit\ClassExistsMock;
18+
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
1819
use Symfony\Component\Config\FileLocator;
1920
use Symfony\Component\Config\Resource\ClassExistenceResource;
2021
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
@@ -41,6 +42,8 @@
4142

4243
class AutowirePassTest extends TestCase
4344
{
45+
use ExpectDeprecationTrait;
46+
4447
public static function setUpBeforeClass(): void
4548
{
4649
ClassExistsMock::register(AutowirePass::class);
@@ -695,8 +698,15 @@ public function testOptionalArgsNoRequiredForCoreClasses()
695698
);
696699
}
697700

698-
public function testSetterInjection()
701+
/**
702+
* @group legacy
703+
*/
704+
public function testSetterInjectionAnnotation()
699705
{
706+
$this->expectDeprecation('Since symfony/dependency-injection 6.3: Relying on the "@required" annotation on method "Symfony\Component\DependencyInjection\Tests\Compiler\SetterInjectionAnnotation::setFoo()" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.');
707+
$this->expectDeprecation('Since symfony/dependency-injection 6.3: Relying on the "@required" annotation on method "Symfony\Component\DependencyInjection\Tests\Compiler\SetterInjectionAnnotation::setChildMethodWithoutDocBlock()" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.');
708+
$this->expectDeprecation('Since symfony/dependency-injection 6.3: Relying on the "@required" annotation on method "Symfony\Component\DependencyInjection\Tests\Compiler\SetterInjectionParentAnnotation::setDependencies()" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.');
709+
700710
$container = new ContainerBuilder();
701711
$container->register(Foo::class);
702712
$container->register(A::class);
@@ -705,7 +715,7 @@ public function testSetterInjection()
705715

706716
// manually configure *one* call, to override autowiring
707717
$container
708-
->register('setter_injection', SetterInjection::class)
718+
->register('setter_injection', SetterInjectionAnnotation::class)
709719
->setAutowired(true)
710720
->addMethodCall('setWithCallsConfigured', ['manual_arg1', 'manual_arg2'])
711721
;
@@ -717,7 +727,7 @@ public function testSetterInjection()
717727
$methodCalls = $container->getDefinition('setter_injection')->getMethodCalls();
718728

719729
$this->assertEquals(
720-
['setWithCallsConfigured', 'setFoo', 'setDependencies', 'setChildMethodWithoutDocBlock'],
730+
['setWithCallsConfigured', 'setFoo', 'setChildMethodWithoutDocBlock', 'setDependencies'],
721731
array_column($methodCalls, 0)
722732
);
723733

@@ -766,7 +776,7 @@ public function testWithNonExistingSetterAndAutowiring()
766776
(new AutowirePass())->process($container);
767777
}
768778

769-
public function testExplicitMethodInjection()
779+
public function testExplicitMethodInjectionAttribute()
770780
{
771781
$container = new ContainerBuilder();
772782
$container->register(Foo::class);
@@ -821,7 +831,33 @@ public function testIgnoreServiceWithClassNotExisting()
821831
$this->assertTrue($container->hasDefinition('bar'));
822832
}
823833

824-
public function testSetterInjectionCollisionThrowsException()
834+
/**
835+
* @group legacy
836+
*/
837+
public function testSetterInjectionFromAnnotationCollisionThrowsException()
838+
{
839+
$this->expectDeprecation('Since symfony/dependency-injection 6.3: Relying on the "@required" annotation on method "Symfony\Component\DependencyInjection\Tests\Compiler\SetterInjectionCollisionAnnotation::setMultipleInstancesForOneArg()" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.');
840+
841+
$container = new ContainerBuilder();
842+
843+
$container->register('c1', CollisionA::class);
844+
$container->register('c2', CollisionB::class);
845+
$aDefinition = $container->register('setter_injection_collision', SetterInjectionCollisionAnnotation::class);
846+
$aDefinition->setAutowired(true);
847+
848+
(new AutowireRequiredMethodsPass())->process($container);
849+
850+
$pass = new AutowirePass();
851+
852+
try {
853+
$pass->process($container);
854+
$this->fail('AutowirePass should have thrown an exception');
855+
} catch (AutowiringFailedException $e) {
856+
$this->assertSame('Cannot autowire service "setter_injection_collision": argument "$collision" of method "Symfony\Component\DependencyInjection\Tests\Compiler\SetterInjectionCollisionAnnotation::setMultipleInstancesForOneArg()" references interface "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" but no such service exists. You should maybe alias this interface to one of these existing services: "c1", "c2".', (string) $e->getMessage());
857+
}
858+
}
859+
860+
public function testSetterInjectionFromAttributeCollisionThrowsException()
825861
{
826862
$container = new ContainerBuilder();
827863

@@ -1129,6 +1165,36 @@ public function testErroredServiceLocator()
11291165
$this->assertSame(['Cannot autowire service "some_locator": it has type "Symfony\Component\DependencyInjection\Tests\Compiler\MissingClass" but this class was not found.'], $container->getDefinition('.errored.some_locator.'.MissingClass::class)->getErrors());
11301166
}
11311167

1168+
/**
1169+
* @group legacy
1170+
*/
1171+
public function testNamedArgumentAliasResolveCollisionsAnnotation()
1172+
{
1173+
$this->expectDeprecation('Since symfony/dependency-injection 6.3: Relying on the "@required" annotation on method "Symfony\Component\DependencyInjection\Tests\Compiler\SetterInjectionCollisionAnnotation::setMultipleInstancesForOneArg()" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.');
1174+
1175+
$container = new ContainerBuilder();
1176+
1177+
$container->register('c1', CollisionA::class);
1178+
$container->register('c2', CollisionB::class);
1179+
$container->setAlias(CollisionInterface::class.' $collision', 'c2');
1180+
$aDefinition = $container->register('setter_injection_collision', SetterInjectionCollisionAnnotation::class);
1181+
$aDefinition->setAutowired(true);
1182+
1183+
(new AutowireRequiredMethodsPass())->process($container);
1184+
1185+
$pass = new AutowirePass();
1186+
1187+
$pass->process($container);
1188+
1189+
$expected = [
1190+
[
1191+
'setMultipleInstancesForOneArg',
1192+
[new TypedReference(CollisionInterface::class.' $collision', CollisionInterface::class)],
1193+
],
1194+
];
1195+
$this->assertEquals($expected, $container->getDefinition('setter_injection_collision')->getMethodCalls());
1196+
}
1197+
11321198
public function testNamedArgumentAliasResolveCollisions()
11331199
{
11341200
$container = new ContainerBuilder();

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

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,37 @@
1212
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
1516
use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredMethodsPass;
1617
use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass;
1718
use Symfony\Component\DependencyInjection\ContainerBuilder;
19+
use Symfony\Component\DependencyInjection\Tests\Fixtures\WitherAnnotationStaticReturnType;
1820
use Symfony\Component\DependencyInjection\Tests\Fixtures\WitherStaticReturnType;
1921

2022
require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php';
2123

2224
class AutowireRequiredMethodsPassTest extends TestCase
2325
{
24-
public function testSetterInjection()
26+
use ExpectDeprecationTrait;
27+
28+
/**
29+
* @group legacy
30+
*/
31+
public function testSetterInjectionAnnotation()
2532
{
33+
$this->expectDeprecation('Since symfony/dependency-injection 6.3: Relying on the "@required" annotation on method "Symfony\Component\DependencyInjection\Tests\Compiler\SetterInjectionAnnotation::setFoo()" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.');
34+
$this->expectDeprecation('Since symfony/dependency-injection 6.3: Relying on the "@required" annotation on method "Symfony\Component\DependencyInjection\Tests\Compiler\SetterInjectionAnnotation::setChildMethodWithoutDocBlock()" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.');
35+
$this->expectDeprecation('Since symfony/dependency-injection 6.3: Relying on the "@required" annotation on method "Symfony\Component\DependencyInjection\Tests\Compiler\SetterInjectionParentAnnotation::setDependencies()" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.');
36+
2637
$container = new ContainerBuilder();
27-
$container->register(Foo::class);
38+
$container->register(FooAnnotation::class);
2839
$container->register(A::class);
2940
$container->register(CollisionA::class);
3041
$container->register(CollisionB::class);
3142

3243
// manually configure *one* call, to override autowiring
3344
$container
34-
->register('setter_injection', SetterInjection::class)
45+
->register('setter_injection', SetterInjectionAnnotation::class)
3546
->setAutowired(true)
3647
->addMethodCall('setWithCallsConfigured', ['manual_arg1', 'manual_arg2']);
3748

@@ -41,7 +52,7 @@ public function testSetterInjection()
4152
$methodCalls = $container->getDefinition('setter_injection')->getMethodCalls();
4253

4354
$this->assertEquals(
44-
['setWithCallsConfigured', 'setFoo', 'setDependencies', 'setChildMethodWithoutDocBlock'],
55+
['setWithCallsConfigured', 'setFoo', 'setChildMethodWithoutDocBlock', 'setDependencies'],
4556
array_column($methodCalls, 0)
4657
);
4758

@@ -70,7 +81,41 @@ public function testSetterInjectionWithAttribute()
7081
$this->assertSame([['setFoo', []]], $methodCalls);
7182
}
7283

73-
public function testExplicitMethodInjection()
84+
/**
85+
* @group legacy
86+
*/
87+
// @deprecated since Symfony 6.3, to be removed in 7.0
88+
public function testExplicitMethodInjectionAnnotation()
89+
{
90+
$this->expectDeprecation('Since symfony/dependency-injection 6.3: Relying on the "@required" annotation on method "Symfony\Component\DependencyInjection\Tests\Compiler\SetterInjectionAnnotation::setFoo()" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.');
91+
$this->expectDeprecation('Since symfony/dependency-injection 6.3: Relying on the "@required" annotation on method "Symfony\Component\DependencyInjection\Tests\Compiler\SetterInjectionAnnotation::setChildMethodWithoutDocBlock()" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.');
92+
$this->expectDeprecation('Since symfony/dependency-injection 6.3: Relying on the "@required" annotation on method "Symfony\Component\DependencyInjection\Tests\Compiler\SetterInjectionParentAnnotation::setDependencies()" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.');
93+
$this->expectDeprecation('Since symfony/dependency-injection 6.3: Relying on the "@required" annotation on method "Symfony\Component\DependencyInjection\Tests\Compiler\SetterInjectionParentAnnotation::setWithCallsConfigured()" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.');
94+
95+
$container = new ContainerBuilder();
96+
$container->register(FooAnnotation::class);
97+
$container->register(A::class);
98+
$container->register(CollisionA::class);
99+
$container->register(CollisionB::class);
100+
101+
$container
102+
->register('setter_injection', SetterInjectionAnnotation::class)
103+
->setAutowired(true)
104+
->addMethodCall('notASetter', []);
105+
106+
(new ResolveClassPass())->process($container);
107+
(new AutowireRequiredMethodsPass())->process($container);
108+
109+
$methodCalls = $container->getDefinition('setter_injection')->getMethodCalls();
110+
111+
$this->assertEquals(
112+
['notASetter', 'setFoo', 'setChildMethodWithoutDocBlock', 'setDependencies', 'setWithCallsConfigured'],
113+
array_column($methodCalls, 0)
114+
);
115+
$this->assertEquals([], $methodCalls[0][1]);
116+
}
117+
118+
public function testExplicitMethodInjectionAttribute()
74119
{
75120
$container = new ContainerBuilder();
76121
$container->register(Foo::class);
@@ -95,13 +140,19 @@ public function testExplicitMethodInjection()
95140
$this->assertEquals([], $methodCalls[0][1]);
96141
}
97142

143+
/**
144+
* @group legacy
145+
*/
98146
public function testWitherInjection()
99147
{
148+
$this->expectDeprecation('Since symfony/dependency-injection 6.3: Relying on the "@required" annotation on method "Symfony\Component\DependencyInjection\Tests\Compiler\WitherAnnotation::withFoo1()" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.');
149+
$this->expectDeprecation('Since symfony/dependency-injection 6.3: Relying on the "@required" annotation on method "Symfony\Component\DependencyInjection\Tests\Compiler\WitherAnnotation::withFoo2()" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.');
150+
100151
$container = new ContainerBuilder();
101-
$container->register(Foo::class);
152+
$container->register(FooAnnotation::class);
102153

103154
$container
104-
->register('wither', Wither::class)
155+
->register('wither', WitherAnnotation::class)
105156
->setAutowired(true);
106157

107158
(new ResolveClassPass())->process($container);
@@ -117,6 +168,33 @@ public function testWitherInjection()
117168
$this->assertSame($expected, $methodCalls);
118169
}
119170

171+
/**
172+
* @group legacy
173+
*/
174+
public function testWitherAnnotationWithStaticReturnTypeInjection()
175+
{
176+
$this< 4E34 /span>->expectDeprecation('Since symfony/dependency-injection 6.3: Relying on the "@required" annotation on method "Symfony\Component\DependencyInjection\Tests\Fixtures\WitherAnnotationStaticReturnType::withFoo()" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.');
177+
$this->expectDeprecation('Since symfony/dependency-injection 6.3: Relying on the "@required" annotation on method "Symfony\Component\DependencyInjection\Tests\Fixtures\WitherAnnotationStaticReturnType::setFoo()" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.');
178+
179+
$container = new ContainerBuilder();
180+
$container->register(FooAnnotation::class);
181+
182+
$container
183+
->register('wither', WitherAnnotationStaticReturnType::class)
184+
->setAutowired(true);
185+
186+
(new ResolveClassPass())->process($container);
187+
(new AutowireRequiredMethodsPass())->process($container);
188+
189+
$methodCalls = $container->getDefinition('wither')->getMethodCalls();
190+
191+
$expected = [
192+
['withFoo', [], true],
193+
['setFoo', []],
194+
];
195+
$this->assertSame($expected, $methodCalls);
196+
}
197+
120198
public function testWitherWithStaticReturnTypeInjection()
121199
{
122200
$container = new ContainerBuilder();

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
1516
use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredPropertiesPass;
1617
use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass;
1718
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -21,8 +22,15 @@
2122

2223
class AutowireRequiredPropertiesPassTest extends TestCase
2324
{
25+
use ExpectDeprecationTrait;
26+
27+
/**
28+
* @group legacy
29+
*/
2430
public function testInjection()
2531
{
32+
$this->expectDeprecation('Since symfony/dependency-injection 6.3: Using the "@required" annotation on property "Symfony\Component\DependencyInjection\Tests\Compiler\PropertiesInjection::$plop" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.');
33+
2634
$container = new ContainerBuilder();
2735
$container->register(Bar::class);
2836
$container->register(A::class);

0 commit comments

Comments
 (0)
0