Skip to content

Commit b26f11c

Browse files
committed
feature #37545 [DependencyInjection] Add the Required attribute (derrabus)
This PR was merged into the 5.2-dev branch. Discussion ---------- [DependencyInjection] Add the Required attribute | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | N/A | License | MIT | Doc PR | TODO This PR proposes a new attribute `#[Required]` that can be used instead of the `@required` annotation. Commits ------- ea26244 [DependencyInjection] Add the Required attribute.
2 parents d0c915c + ea26244 commit b26f11c

File tree

16 files changed

+175
-11
lines changed

16 files changed

+175
-11
lines changed

.github/patch-types.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
case false !== strpos($file, '/src/Symfony/Component/Debug/Tests/Fixtures/'):
2323
case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Compiler/OptionalServiceClass.php'):
2424
case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php'):
25+
case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_80.php'):
2526
case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/uniontype_classes.php'):
2627
case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/ParentNotExists.php'):
2728
case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/BadClasses/MissingParent.php'):

src/Symfony/Component/DependencyInjection/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)) {

src/Symfony/Component/DependencyInjection/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)) {

src/Symfony/Component/DependencyInjection/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);

src/Symfony/Component/DependencyInjection/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
}

src/Symfony/Component/DependencyInjection/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
}

src/Symfony/Component/DependencyInjection/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+
}

src/Symfony/Contracts/Cache/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"minimum-stability": "dev",
2929
"extra": {
3030
"branch-alias": {
31-
"dev-master": "2.1-dev"
31+
"dev-master": "2.2-dev"
3232
},
3333
"thanks": {
3434
"name": "symfony/contracts",

src/Symfony/Contracts/Deprecation/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"minimum-stability": "dev",
2626
"extra": {
2727
"branch-alias": {
28-
"dev-master": "2.1-dev"
28+
"dev-master": "2.2-dev"
2929
},
3030
"thanks": {
3131
"name": "symfony/contracts",

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy