diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index 61e2c135a1037..59be45c99780d 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -17,6 +17,7 @@ CHANGELOG * Add `#[Exclude]` to skip autoregistering a class * Add support for autowiring services as closures using `#[AutowireCallable]` or `#[AutowireServiceClosure]` * Deprecate `#[MapDecorated]`, use `#[AutowireDecorated]` instead + * Deprecate the `@required` annotation, use the `Symfony\Contracts\Service\Attribute\Required` attribute instead 6.2 --- diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php index da2fc5947d938..a3f5199ef20c8 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredMethodsPass.php @@ -15,7 +15,7 @@ use Symfony\Contracts\Service\Attribute\Required; /** - * Looks for definitions with autowiring enabled and registers their corresponding "@required" methods as setters. + * Looks for definitions with autowiring enabled and registers their corresponding "#[Required]" methods as setters. * * @author Nicolas Grekas
*/
@@ -57,6 +57,8 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed
}
if (false !== $doc = $r->getDocComment()) {
if (false !== stripos($doc, '@required') && preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) {
+ 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);
+
if ($this->isWither($reflectionMethod, $doc)) {
$withers[] = [$reflectionMethod->name, [], true];
} else {
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredPropertiesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredPropertiesPass.php
index d77e9955b004d..0f093bb7fc702 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredPropertiesPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredPropertiesPass.php
@@ -17,7 +17,7 @@
use Symfony\Contracts\Service\Attribute\Required;
/**
- * Looks for definitions with autowiring enabled and registers their corresponding "@required" properties.
+ * Looks for definitions with autowiring enabled and registers their corresponding "#[Required]" properties.
*
* @author Sebastien Morel (Plopix)
@@ -40,11 +40,15 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed
if (!($type = $reflectionProperty->getType()) instanceof \ReflectionNamedType) {
continue;
}
+ $doc = false;
if (!$reflectionProperty->getAttributes(Required::class)
&& ((false === $doc = $reflectionProperty->getDocComment()) || false === stripos($doc, '@required') || !preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc))
) {
continue;
}
+ if ($doc) {
+ 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);
+ }
if (\array_key_exists($name = $reflectionProperty->getName(), $properties)) {
continue;
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php
index bdf07b10ff077..bd51657278526 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php
@@ -15,6 +15,7 @@
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Bridge\PhpUnit\ClassExistsMock;
+use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Resource\ClassExistenceResource;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
@@ -41,6 +42,8 @@
class AutowirePassTest extends TestCase
{
+ use ExpectDeprecationTrait;
+
public static function setUpBeforeClass(): void
{
ClassExistsMock::register(AutowirePass::class);
@@ -695,8 +698,15 @@ public function testOptionalArgsNoRequiredForCoreClasses()
);
}
- public function testSetterInjection()
+ /**
+ * @group legacy
+ */
+ public function testSetterInjectionAnnotation()
{
+ $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.');
+ $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.');
+ $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.');
+
$container = new ContainerBuilder();
$container->register(Foo::class);
$container->register(A::class);
@@ -705,7 +715,7 @@ public function testSetterInjection()
// manually configure *one* call, to override autowiring
$container
- ->register('setter_injection', SetterInjection::class)
+ ->register('setter_injection', SetterInjectionAnnotation::class)
->setAutowired(true)
->addMethodCall('setWithCallsConfigured', ['manual_arg1', 'manual_arg2'])
;
@@ -717,7 +727,7 @@ public function testSetterInjection()
$methodCalls = $container->getDefinition('setter_injection')->getMethodCalls();
$this->assertEquals(
- ['setWithCallsConfigured', 'setFoo', 'setDependencies', 'setChildMethodWithoutDocBlock'],
+ ['setWithCallsConfigured', 'setFoo', 'setChildMethodWithoutDocBlock', 'setDependencies'],
array_column($methodCalls, 0)
);
@@ -766,7 +776,7 @@ public function testWithNonExistingSetterAndAutowiring()
(new AutowirePass())->process($container);
}
- public function testExplicitMethodInjection()
+ public function testExplicitMethodInjectionAttribute()
{
$container = new ContainerBuilder();
$container->register(Foo::class);
@@ -821,7 +831,33 @@ public function testIgnoreServiceWithClassNotExisting()
$this->assertTrue($container->hasDefinition('bar'));
}
- public function testSetterInjectionCollisionThrowsException()
+ /**
+ * @group legacy
+ */
+ public function testSetterInjectionFromAnnotationCollisionThrowsException()
+ {
+ $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.');
+
+ $container = new ContainerBuilder();
+
+ $container->register('c1', CollisionA::class);
+ $container->register('c2', CollisionB::class);
+ $aDefinition = $container->register('setter_injection_collision', SetterInjectionCollisionAnnotation::class);
+ $aDefinition->setAutowired(true);
+
+ (new AutowireRequiredMethodsPass())->process($container);
+
+ $pass = new AutowirePass();
+
+ try {
+ $pass->process($container);
+ $this->fail('AutowirePass should have thrown an exception');
+ } catch (AutowiringFailedException $e) {
+ $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());
+ }
+ }
+
+ public function testSetterInjectionFromAttributeCollisionThrowsException()
{
$container = new ContainerBuilder();
@@ -1129,6 +1165,36 @@ public function testErroredServiceLocator()
$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());
}
+ /**
+ * @group legacy
+ */
+ public function testNamedArgumentAliasResolveCollisionsAnnotation()
+ {
+ $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.');
+
+ $container = new ContainerBuilder();
+
+ $container->register('c1', CollisionA::class);
+ $container->register('c2', CollisionB::class);
+ $container->setAlias(CollisionInterface::class.' $collision', 'c2');
+ $aDefinition = $container->register('setter_injection_collision', SetterInjectionCollisionAnnotation::class);
+ $aDefinition->setAutowired(true);
+
+ (new AutowireRequiredMethodsPass())->process($container);
+
+ $pass = new AutowirePass();
+
+ $pass->process($container);
+
+ $expected = [
+ [
+ 'setMultipleInstancesForOneArg',
+ [new TypedReference(CollisionInterface::class.' $collision', CollisionInterface::class)],
+ ],
+ ];
+ $this->assertEquals($expected, $container->getDefinition('setter_injection_collision')->getMethodCalls());
+ }
+
public function testNamedArgumentAliasResolveCollisions()
{
$container = new ContainerBuilder();
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredMethodsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredMethodsPassTest.php
index 9cedd5e02f249..73f9f62bbad75 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredMethodsPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredMethodsPassTest.php
@@ -12,26 +12,37 @@
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use PHPUnit\Framework\TestCase;
+use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredMethodsPass;
use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Tests\Fixtures\WitherAnnotationStaticReturnType;
use Symfony\Component\DependencyInjection\Tests\Fixtures\WitherStaticReturnType;
require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php';
class AutowireRequiredMethodsPassTest extends TestCase
{
- public function testSetterInjection()
+ use ExpectDeprecationTrait;
+
+ /**
+ * @group legacy
+ */
+ public function testSetterInjectionAnnotation()
{
+ $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.');
+ $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.');
+ $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.');
+
$container = new ContainerBuilder();
- $container->register(Foo::class);
+ $container->register(FooAnnotation::class);
$container->register(A::class);
$container->register(CollisionA::class);
$container->register(CollisionB::class);
// manually configure *one* call, to override autowiring
$container
- ->register('setter_injection', SetterInjection::class)
+ ->register('setter_injection', SetterInjectionAnnotation::class)
->setAutowired(true)
->addMethodCall('setWithCallsConfigured', ['manual_arg1', 'manual_arg2']);
@@ -41,7 +52,7 @@ public function testSetterInjection()
$methodCalls = $container->getDefinition('setter_injection')->getMethodCalls();
$this->assertEquals(
- ['setWithCallsConfigured', 'setFoo', 'setDependencies', 'setChildMethodWithoutDocBlock'],
+ ['setWithCallsConfigured', 'setFoo', 'setChildMethodWithoutDocBlock', 'setDependencies'],
array_column($methodCalls, 0)
);
@@ -70,7 +81,41 @@ public function testSetterInjectionWithAttribute()
$this->assertSame([['setFoo', []]], $methodCalls);
}
- public function testExplicitMethodInjection()
+ /**
+ * @group legacy
+ */
+ // @deprecated since Symfony 6.3, to be removed in 7.0
+ public function testExplicitMethodInjectionAnnotation()
+ {
+ $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.');
+ $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.');
+ $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.');
+ $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.');
+
+ $container = new ContainerBuilder();
+ $container->register(FooAnnotation::class);
+ $container->register(A::class);
+ $container->register(CollisionA::class);
+ $container->register(CollisionB::class);
+
+ $container
+ ->register('setter_injection', SetterInjectionAnnotation::class)
+ ->setAutowired(true)
+ ->addMethodCall('notASetter', []);
+
+ (new ResolveClassPass())->process($container);
+ (new AutowireRequiredMethodsPass())->process($container);
+
+ $methodCalls = $container->getDefinition('setter_injection')->getMethodCalls();
+
+ $this->assertEquals(
+ ['notASetter', 'setFoo', 'setChildMethodWithoutDocBlock', 'setDependencies', 'setWithCallsConfigured'],
+ array_column($methodCalls, 0)
+ );
+ $this->assertEquals([], $methodCalls[0][1]);
+ }
+
+ public function testExplicitMethodInjectionAttribute()
{
$container = new ContainerBuilder();
$container->register(Foo::class);
@@ -95,13 +140,19 @@ public function testExplicitMethodInjection()
$this->assertEquals([], $methodCalls[0][1]);
}
+ /**
+ * @group legacy
+ */
public function testWitherInjection()
{
+ $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.');
+ $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.');
+
$container = new ContainerBuilder();
- $container->register(Foo::class);
+ $container->register(FooAnnotation::class);
$container
- ->register('wither', Wither::class)
+ ->register('wither', WitherAnnotation::class)
->setAutowired(true);
(new ResolveClassPass())->process($container);
@@ -117,6 +168,33 @@ public function testWitherInjection()
$this->assertSame($expected, $methodCalls);
}
+ /**
+ * @group legacy
+ */
+ public function testWitherAnnotationWithStaticReturnTypeInjection()
+ {
+ $this->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.');
+ $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.');
+
+ $container = new ContainerBuilder();
+ $container->register(FooAnnotation::class);
+
+ $container
+ ->register('wither', WitherAnnotationStaticReturnType::class)
+ ->setAutowired(true);
+
+ (new ResolveClassPass())->process($container);
+ (new AutowireRequiredMethodsPass())->process($container);
+
+ $methodCalls = $container->getDefinition('wither')->getMethodCalls();
+
+ $expected = [
+ ['withFoo', [], true],
+ ['setFoo', []],
+ ];
+ $this->assertSame($expected, $methodCalls);
+ }
+
public function testWitherWithStaticReturnTypeInjection()
{
$container = new ContainerBuilder();
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredPropertiesPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredPropertiesPassTest.php
index 2cee2e7ed5c35..62e12f9e84cd8 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredPropertiesPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredPropertiesPassTest.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use PHPUnit\Framework\TestCase;
+use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredPropertiesPass;
use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -21,8 +22,15 @@
class AutowireRequiredPropertiesPassTest extends TestCase
{
+ use ExpectDeprecationTrait;
+
+ /**
+ * @group legacy
+ */
public function testInjection()
{
+ $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.');
+
$container = new ContainerBuilder();
$container->register(Bar::class);
$container->register(A::class);
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php
index 497acee2f779c..449b60e5bccab 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use PHPUnit\Framework\TestCase;
+use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
@@ -37,6 +38,8 @@
class ResolveBindingsPassTest extends TestCase
{
+ use ExpectDeprecationTrait;
+
public function testProcess()
{
$container = new ContainerBuilder();
@@ -143,7 +146,25 @@ public function testTypedReferenceSupport()
$this->assertEquals([new Reference('bar')], $container->getDefinition('def3')->getArguments());
}
- public function testScalarSetter()
+ /**
+ * @group legacy
+ */
+ public function testScalarSetterAnnotation()
+ {
+ $this->expectDeprecation('Since symfony/dependency-injection 6.3: Relying on the "@required" annotation on method "Symfony\Component\DependencyInjection\Tests\Compiler\ScalarSetterAnnotation::setDefaultLocale()" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.');
+
+ $container = new ContainerBuilder();
+
+ $definition = $container->autowire('foo', ScalarSetterAnnotation::class);
+ $definition->setBindings(['$defaultLocale' => 'fr']);
+
+ (new AutowireRequiredMethodsPass())->process($container);
+ (new ResolveBindingsPass())->process($container);
+
+ $this->assertEquals([['setDefaultLocale', ['fr']]], $definition->getMethodCalls());
+ }
+
+ public function testScalarSetterAttribute()
{
$container = new ContainerBuilder();
diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
index f3d0bb270cb19..4ec0624b86288 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
@@ -48,6 +48,7 @@
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\Tests\Compiler\Foo;
+use Symfony\Component\DependencyInjection\Tests\Compiler\FooAnnotation;
use Symfony\Component\DependencyInjection\Tests\Compiler\Wither;
use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass;
use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition;
@@ -55,6 +56,7 @@
use Symfony\Component\DependencyInjection\Tests\Fixtures\ScalarFactory;
use Symfony\Component\DependencyInjection\Tests\Fixtures\SimilarArgumentsDummy;
use Symfony\Component\DependencyInjection\Tests\Fixtures\StringBackedEnum;
+use Symfony\Component\DependencyInjection\Tests\Fixtures\WitherAnnotationStaticReturnType;
use Symfony\Component\DependencyInjection\Tests\Fixtures\WitherStaticReturnType;
use Symfony\Component\DependencyInjection\TypedReference;
use Symfony\Component\ExpressionLanguage\Expression;
@@ -1832,6 +1834,28 @@ public function testLazyWither()
$this->assertInstanceOf(Wither::class, $wither->withFoo1($wither->foo));
}
+ /**
+ * @group legacy
+ */
+ public function testWitherAnnotationWithStaticReturnType()
+ {
+ $this->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.');
+ $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.');
+
+ $container = new ContainerBuilder();
+ $container->register(FooAnnotation::class);
+
+ $container
+ ->register('wither', WitherAnnotationStaticReturnType::class)
+ ->setPublic(true)
+ ->setAutowired(true);
+
+ $container->compile();
+
+ $wither = $container->get('wither');
+ $this->assertInstanceOf(FooAnnotation::class, $wither->foo);
+ }
+
public function testWitherWithStaticReturnType()
{
$container = new ContainerBuilder();
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
index 5e313cad403c9..1cc9d514fc37a 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
@@ -44,7 +44,9 @@
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\Tests\Compiler\Foo;
+use Symfony\Component\DependencyInjection\Tests\Compiler\FooAnnotation;
use Symfony\Component\DependencyInjection\Tests\Compiler\Wither;
+use Symfony\Component\DependencyInjection\Tests\Compiler\WitherAnnotation;
use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition;
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooClassWithEnumAttribute;
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum;
@@ -1447,7 +1449,38 @@ public function testAliasCanBeFoundInTheDumpedContainerWhenBothTheAliasAndTheSer
$this->assertContains('bar', $service_ids);
}
- public function testWither()
+ /**
+ * @group legacy
+ */
+ public function testWitherAnnotation()
+ {
+ $this->expectDeprecation('Since symfony/dependency-injection 6.3: Relying on the "@required" annotation on method "Symfony\Component\DependencyInjection\Tests\Compiler\FooAnnotation::cloneFoo()" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.');
+ $this->expectDeprecation('Since symfony/dependency-injection 6.3: Relying on the "@required" annotation on method "Symfony\Component\DependencyInjection\Tests\Compiler\WitherAnnotation::setFoo()" is deprecated, use the "Symfony\Contracts\Service\Attribute\Required" attribute instead.');
+ $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.');
+ $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.');
+
+ $container = new ContainerBuilder();
+ $container->register(FooAnnotation::class)
+ ->setAutowired(true);
+
+ $container
+ ->register('wither', WitherAnnotation::class)
+ ->setPublic(true)
+ ->setAutowired(true);
+
+ $container->compile();
+ $dumper = new PhpDumper($container);
+ $dump = $dumper->dump(['class' => 'Symfony_DI_PhpDumper_Service_Wither_Annotation']);
+ $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_wither_annotation.php', $dump);
+ eval('?>'.$dump);
+
+ $container = new \Symfony_DI_PhpDumper_Service_Wither_Annotation();
+
+ $wither = $container->get('wither');
+ $this->assertInstanceOf(FooAnnotation::class, $wither->foo);
+ }
+
+ public function testWitherAttribute()
{
$container = new ContainerBuilder();
$container->register(Foo::class)
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/WitherAnnotationStaticReturnType.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/WitherAnnotationStaticReturnType.php
new file mode 100644
index 0000000000000..14b76d3b202f2
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/WitherAnnotationStaticReturnType.php
@@ -0,0 +1,34 @@
+foo = $foo;
+
+ return $new;
+ }
+
+ /**
+ * @required
+ *
+ * @return $this
+ */
+ public function setFoo(FooAnnotation $foo): static
+ {
+ $this->foo = $foo;
+
+ return $this;
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/WitherStaticReturnType.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/WitherStaticReturnType.php
index 1236b75dfad24..60063cceb152a 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/WitherStaticReturnType.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/WitherStaticReturnType.php
@@ -3,14 +3,13 @@
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
use Symfony\Component\DependencyInjection\Tests\Compiler\Foo;
+use Symfony\Contracts\Service\Attribute\Required;
class WitherStaticReturnType
{
public $foo;
- /**
- * @required
- */
+ #[Required]
public function withFoo(Foo $foo): static
{
$new = clone $this;
@@ -20,9 +19,9 @@ public function withFoo(Foo $foo): static
}
/**
- * @required
* @return $this
*/
+ #[Required]
public function setFoo(Foo $foo): static
{
$this->foo = $foo;
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php
index 87f5ab65a1277..70c46ecb4fe64 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php
@@ -3,6 +3,7 @@
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use Psr\Log\LoggerInterface;
+use Symfony\Contracts\Service\Attribute\Required;
require __DIR__.'/uniontype_classes.php';
require __DIR__.'/autowiring_classes_80.php';
@@ -11,7 +12,8 @@
require __DIR__.'/compositetype_classes.php';
}
-class Foo
+// @deprecated since Symfony 6.3, to be removed in 7.0
+class FooAnnotation
{
/**
* @required
@@ -22,6 +24,15 @@ public function cloneFoo(): static
}
}
+class Foo
+{
+ #[Required]
+ public function cloneFoo(): static
+ {
+ return clone $this;
+ }
+}
+
class Bar
{
public function __construct(Foo $foo)
@@ -225,7 +236,8 @@ public function __construct($foo, Bar $bar, $baz)
}
}
-class SetterInjectionCollision
+// @deprecated since Symfony 6.3, to be removed in 7.0
+class SetterInjectionCollisionAnnotation
{
/**
* @required
@@ -238,8 +250,21 @@ public function setMultipleInstancesForOneArg(CollisionInterface $collision)
}
}
-class SetterInjection extends SetterInjectionParent
+class SetterInjectionCollision
+{
+ #[Required]
+ public function setMultipleInstancesForOneArg(CollisionInterface $collision)
+ {
+ // The CollisionInterface cannot be autowired - there are multiple
+
+ // should throw an exception
+ }
+}
+
+// @deprecated since Symfony 6.3, to be removed in 7.0
+class SetterInjectionAnnotation extends SetterInjectionParentAnnotation
{
+
/**
* @required
*/
@@ -248,6 +273,27 @@ public function setFoo(Foo $foo)
// should be called
}
+ public function notASetter(A $a)
+ {
+ // should be called only when explicitly specified
+ }
+
+ /**
+ * @required*/
+ public function setChildMethodWithoutDocBlock(A $a)
+ {
+ }
+}
+
+// @deprecated since Symfony 6.3, to be removed in 7.0
+class SetterInjection extends SetterInjectionParent
+{
+ #[Required]
+ public function setFoo(Foo $foo)
+ {
+ // should be called
+ }
+
/** @inheritdoc*/ // <- brackets are missing on purpose
public function setDependencies(Foo $foo, A $a)
{
@@ -264,29 +310,24 @@ public function notASetter(A $a)
{
// should be called only when explicitly specified
}
-
- /**
- * @required*/
- public function setChildMethodWithoutDocBlock(A $a)
- {
- }
}
-class Wither
+// @deprecated since Symfony 6.3, to be removed in 7.0
+class WitherAnnotation
{
public $foo;
/**
* @required
*/
- public function setFoo(Foo $foo)
+ public function setFoo(FooAnnotation $foo)
{
}
/**
* @required
*/
- public function withFoo1(Foo $foo): static
+ public function withFoo1(FooAnnotation $foo): static
{
return $this->withFoo2($foo);
}
@@ -294,6 +335,31 @@ public function withFoo1(Foo $foo): static
/**
* @required
*/
+ public function withFoo2(FooAnnotation $foo): static
+ {
+ $new = clone $this;
+ $new->foo = $foo;
+
+ return $new;
+ }
+}
+
+class Wither
+{
+ public $foo;
+
+ #[Required]
+ public function setFoo(Foo $foo)
+ {
+ }
+
+ #[Required]
+ public function withFoo1(Foo $foo): static
+ {
+ return $this->withFoo2($foo);
+ }
+
+ #[Required]
public function withFoo2(Foo $foo): static
{
$new = clone $this;
@@ -303,7 +369,8 @@ public function withFoo2(Foo $foo): static
}
}
-class SetterInjectionParent
+// @deprecated since Symfony 6.3, to be removed in 7.0
+class SetterInjectionParentAnnotation
{
/** @required*/
public function setDependencies(Foo $foo, A $a)
@@ -327,6 +394,30 @@ public function setChildMethodWithoutDocBlock(A $a)
}
}
+class SetterInjectionParent
+{
+ #[Required]
+ public function setDependencies(Foo $foo, A $a)
+ {
+ // should be called
+ }
+
+ public function notASetter(A $a)
+ {
+ // #[Required] should be ignored when the child does not add @inheritdoc
+ }
+
+ #[Required]
+ public function setWithCallsConfigured(A $a)
+ {
+ }
+
+ #[Required]
+ public function setChildMethodWithoutDocBlock(A $a)
+ {
+ }
+}
+
class NotWireable
{
public function setNotAutowireable(NotARealClass $n)
@@ -357,7 +448,7 @@ public function setOptionalArgNoAutowireable($other = 'default_val')
{
}
- /** @required */
+ #[Required]
protected function setProtectedMethod(A $a)
{
}
@@ -370,7 +461,8 @@ private function __construct()
}
}
-class ScalarSetter
+// @deprecated since Symfony 6.3, to be removed in 7.0
+class ScalarSetterAnnotation
{
/**
* @required
@@ -380,6 +472,14 @@ public function setDefaultLocale($defaultLocale)
}
}
+class ScalarSetter
+{
+ #[Required]
+ public function setDefaultLocale($defaultLocale)
+ {
+ }
+}
+
interface DecoratorInterface
{
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_74.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_74.php
index 60b7fa7ca0c89..8e354b28219a1 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_74.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_74.php
@@ -2,6 +2,7 @@
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
+// @deprecated since Symfony 6.3, to be removed in 7.0
class PropertiesInjection
{
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_wither_annotation.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_wither_annotation.php
new file mode 100644
index 0000000000000..a958df8ebdc0d
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_wither_annotation.php
@@ -0,0 +1,66 @@
+ref = \WeakReference::create($this);
+ $this->services = $this->privates = [];
+ $this->methodMap = [
+ 'wither' => 'getWitherService',
+ ];
+
+ $this->aliases = [];
+ }
+
+ public function compile(): void
+ {
+ throw new LogicException('You cannot compile a dumped container that was already compiled.');
+ }
+
+ public function isCompiled(): bool
+ {
+ return true;
+ }
+
+ public function getRemovedIds(): array
+ {
+ return [
+ 'Symfony\\Component\\DependencyInjection\\Tests\\Compiler\\FooAnnotation' => true,
+ ];
+ }
+
+ /**
+ * Gets the public 'wither' shared autowired service.
+ *
+ * @return \Symfony\Component\DependencyInjection\Tests\Compiler\WitherAnnotation
+ */
+ protected static function getWitherService($container)
+ {
+ $instance = new \Symfony\Component\DependencyInjection\Tests\Compiler\WitherAnnotation();
+
+ $a = new \Symfony\Component\DependencyInjection\Tests\Compiler\FooAnnotation();
+ $a = $a->cloneFoo();
+
+ $instance = $instance->withFoo1($a);
+ $container->services['wither'] = $instance = $instance->withFoo2($a);
+ $instance->setFoo($a);
+
+ return $instance;
+ }
+}