Skip to content

Commit 989db6c

Browse files
committed
[DependencyInjection] Add the Required attribute.
1 parent a26234d commit 989db6c

7 files changed

+140
-4
lines changed

Compiler/AutowireRequiredMethodsPass.php

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

1414
use Symfony\Component\DependencyInjection\Definition;
15+
use Symfony\Contracts\Service\Attribute\Required;
1516

1617
/**
1718
* Looks for definitions with autowiring enabled and registers their corresponding "@required" methods as setters.
@@ -49,6 +50,14 @@ protected function processValue($value, bool $isRoot = false)
4950
}
5051

5152
while (true) {
53+
if (\PHP_VERSION_ID >= 80000 && $r->getAttributes(Required::class)) {
54+
if ($this->isWither($r, $r->getDocComment() ?: '')) {
55+
$withers[] = [$r->name, [], true];
56+
} else {
57+
$value->addMethodCall($r->name, []);
58+
}
59+
break;
60+
}
5261
if (false !== $doc = $r->getDocComment()) {
5362
if (false !== stripos($doc, '@required') && preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) {
5463
if ($this->isWither($reflectionMethod, $doc)) {

Compiler/AutowireRequiredPropertiesPass.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\DependencyInjection\ContainerInterface;
1515
use Symfony\Component\DependencyInjection\Definition;
1616
use Symfony\Component\DependencyInjection\TypedReference;
17+
use Symfony\Contracts\Service\Attribute\Required;
1718

1819
/**
1920
* Looks for definitions with autowiring enabled and registers their corresponding "@required" properties.
@@ -45,10 +46,9 @@ protected function processValue($value, bool $isRoot = false)
4546
if (!($type = $reflectionProperty->getType()) instanceof \ReflectionNamedType) {
4647
continue;
4748
}
48-
if (false === $doc = $reflectionProperty->getDocComment()) {
49-
continue;
50-
}
51-
if (false === stripos($doc, '@required') || !preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) {
49+
if ((\PHP_VERSION_ID < 80000 || !$reflectionProperty->getAttributes(Required::class))
50+
&& ((false === $doc = $reflectionProperty->getDocComment()) || false === stripos($doc, '@required') || !preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc))
51+
) {
5252
continue;
5353
}
5454
if (\array_key_exists($name = $reflectionProperty->getName(), $properties)) {

Tests/Compiler/AutowirePassTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
use Symfony\Component\DependencyInjection\Tests\Fixtures\includes\FooVariadic;
2929
use Symfony\Component\DependencyInjection\Tests\Fixtures\includes\MultipleArgumentsOptionalScalarNotReallyOptional;
3030
use Symfony\Component\DependencyInjection\TypedReference;
31+
use Symfony\Contracts\Service\Attribute\Required;
3132

3233
require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php';
3334

@@ -640,6 +641,32 @@ public function testSetterInjection()
640641
);
641642
}
642643

644+
/**
645+
* @requires PHP 8
646+
*/
647+
public function testSetterInjectionWithAttribute()
648+
{
649+
if (!class_exists(Required::class)) {
650+
$this->markTestSkipped('symfony/service-contracts 2.2 required');
651+
}
652+
653+
$container = new ContainerBuilder();
654+
$container->register(Foo::class);
655+
656+
$container
657+
->register('setter_injection', AutowireSetter::class)
658+
->setAutowired(true);
659+
660+
(new ResolveClassPass())->process($container);
661+
(new AutowireRequiredMethodsPass())->process($container);
662+
(new AutowirePass())->process($container);
663+
664+
$methodCalls = $container->getDefinition('setter_injection')->getMethodCalls();
665+
$this->assertCount(1, $methodCalls);
666+
$this->assertSame('setFoo', $methodCalls[0][0]);
667+
$this->assertSame(Foo::class, (string) $methodCalls[0][1][0]);
668+
}
669+
643670
public function testWithNonExistingSetterAndAutowiring()
644671
{
645672
$this->expectException(RuntimeException::class);

Tests/Compiler/AutowireRequiredMethodsPassTest.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass;
1717
use Symfony\Component\DependencyInjection\ContainerBuilder;
1818
use Symfony\Component\DependencyInjection\Tests\Fixtures\WitherStaticReturnType;
19+
use Symfony\Contracts\Service\Attribute\Required;
1920

2021
require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php';
2122

@@ -54,6 +55,29 @@ public function testSetterInjection()
5455
$this->assertEquals([], $methodCalls[1][1]);
5556
}
5657

58+
/**
59+
* @requires PHP 8
60+
*/
61+
public function testSetterInjectionWithAttribute()
62+
{
63+
if (!class_exists(Required::class)) {
64+
$this->markTestSkipped('symfony/service-contracts 2.2 required');
65+
}
66+
67+
$container = new ContainerBuilder();
68+
$container->register(Foo::class);
69+
70+
$container
71+
->register('setter_injection', AutowireSetter::class)
72+
->setAutowired(true);
73+
74+
(new ResolveClassPass())->process($container);
75+
(new AutowireRequiredMethodsPass())->process($container);
76+
77+
$methodCalls = $container->getDefinition('setter_injection')->getMethodCalls();
78+
$this->assertSame([['setFoo', []]], $methodCalls);
79+
}
80+
5781
public function testExplicitMethodInjection()
5882
{
5983
$container = new ContainerBuilder();
@@ -124,4 +148,26 @@ public function testWitherWithStaticReturnTypeInjection()
124148
];
125149
$this->assertSame($expected, $methodCalls);
126150
}
151+
152+
/**
153+
* @requires PHP 8
154+
*/
155+
public function testWitherInjectionWithAttribute()
156+
{
157+
if (!class_exists(Required::class)) {
158+
$this->markTestSkipped('symfony/service-contracts 2.2 required');
159+
}
160+
161+
$container = new ContainerBuilder();
162+
$container->register(Foo::class);
163+
164+
$container
165+
->register('wither', AutowireWither::class)
166+
->setAutowired(true);
167+
168+
(new ResolveClassPass())->process($container);
169+
(new AutowireRequiredMethodsPass())->process($container);
170+
171+
$this->assertSame([['withFoo', [], true]], $container->getDefinition('wither')->getMethodCalls());
172+
}
127173
}

Tests/Compiler/AutowireRequiredPropertiesPassTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredPropertiesPass;
1616
use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass;
1717
use Symfony\Component\DependencyInjection\ContainerBuilder;
18+
use Symfony\Contracts\Service\Attribute\Required;
1819

1920
require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php';
2021

@@ -43,4 +44,28 @@ public function testInjection()
4344
$this->assertArrayHasKey('plop', $properties);
4445
$this->assertEquals(Bar::class, (string) $properties['plop']);
4546
}
47+
48+
/**
49+
* @requires PHP 8
50+
*/
51+
public function testAttribute()
52+
{
53+
if (!class_exists(Required::class)) {
54+
$this->markTestSkipped('symfony/service-contracts 2.2 required');
55+
}
56+
57+
$container = new ContainerBuilder();
58+
$container->register(Foo::class);
59+
60+
$container->register('property_injection', AutowireProperty::class)
61+
->setAutowired(true);
62+
63+
(new ResolveClassPass())->process($container);
64+
(new AutowireRequiredPropertiesPass())->process($container);
65+
66+
$properties = $container->getDefinition('property_injection')->getProperties();
67+
68+
$this->assertArrayHasKey('foo', $properties);
69+
$this->assertEquals(Foo::class, (string) $properties['foo']);
70+
}
4671
}

Tests/Fixtures/includes/autowiring_classes.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
if (PHP_VERSION_ID >= 80000) {
88
require __DIR__.'/uniontype_classes.php';
9+
require __DIR__.'/autowiring_classes_80.php';
910
}
1011

1112
class Foo
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
4+
5+
use Symfony\Contracts\Service\Attribute\Required;
6+
7+
class AutowireSetter
8+
{
9+
#[Required]
10+
public function setFoo(Foo $foo): void
11+
{
12+
}
13+
}
14+
15+
class AutowireWither
16+
{
17+
#[Required]
18+
public function withFoo(Foo $foo): static
19+
{
20+
return $this;
21+
}
22+
}
23+
24+
class AutowireProperty
25+
{
26+
#[Required]
27+
public Foo $foo;
28+
}

0 commit comments

Comments
 (0)