Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4a7aa8e

Browse files
kbondnicolas-grekas
authored andcommitted
[DependencyInjection] Add SubscribedService attribute, deprecate current ServiceSubscriberTrait usage
1 parent fe48ba6 commit 4a7aa8e

11 files changed

+334
-14
lines changed

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

Lines changed: 116 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,16 @@
2424
use Symfony\Component\DependencyInjection\Reference;
2525
use Symfony\Component\DependencyInjection\ServiceLocator;
2626
use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition;
27+
use Symfony\Component\DependencyInjection\Tests\Fixtures\LegacyTestServiceSubscriberChild;
28+
use Symfony\Component\DependencyInjection\Tests\Fixtures\LegacyTestServiceSubscriberParent;
2729
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition1;
2830
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition2;
2931
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition3;
3032
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber;
3133
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriberChild;
3234
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriberParent;
3335
use Symfony\Component\DependencyInjection\TypedReference;
36+
use Symfony\Contracts\Service\Attribute\SubscribedService;
3437
use Symfony\Contracts\Service\ServiceSubscriberInterface;
3538
use Symfony\Contracts\Service\ServiceSubscriberTrait;
3639

@@ -143,11 +146,14 @@ public function testExtraServiceSubscriber()
143146
$container->compile();
144147
}
145148

149+
/**
150+
* @group legacy
151+
*/
146152
public function testServiceSubscriberTrait()
147153
{
148154
$container = new ContainerBuilder();
149155

150-
$container->register('foo', TestServiceSubscriberChild::class)
156+
$container->register('foo', LegacyTestServiceSubscriberChild::class)
151157
->addMethodCall('setContainer', [new Reference(PsrContainerInterface::class)])
152158
->addTag('container.service_subscriber')
153159
;
@@ -159,15 +165,18 @@ public function testServiceSubscriberTrait()
159165
$locator = $container->getDefinition((string) $foo->getMethodCalls()[0][1][0]);
160166

161167
$expected = [
162-
TestServiceSubscriberChild::class.'::invalidDefinition' => new ServiceClosureArgument(new TypedReference('Symfony\Component\DependencyInjection\Tests\Fixtures\InvalidDefinition', 'Symfony\Component\DependencyInjection\Tests\Fixtures\InvalidDefinition', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
163-
TestServiceSubscriberChild::class.'::testDefinition2' => new ServiceClosureArgument(new TypedReference(TestDefinition2::class, TestDefinition2::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
164-
TestServiceSubscriberChild::class.'::testDefinition3' => new ServiceClosureArgument(new TypedReference(TestDefinition3::class, TestDefinition3::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
165-
TestServiceSubscriberParent::class.'::testDefinition1' => new ServiceClosureArgument(new TypedReference(TestDefinition1::class, TestDefinition1::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
168+
LegacyTestServiceSubscriberChild::class.'::invalidDefinition' => new ServiceClosureArgument(new TypedReference('Symfony\Component\DependencyInjection\Tests\Fixtures\InvalidDefinition', 'Symfony\Component\DependencyInjection\Tests\Fixtures\InvalidDefinition', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
169+
LegacyTestServiceSubscriberChild::class.'::testDefinition2' => new ServiceClosureArgument(new TypedReference(TestDefinition2::class, TestDefinition2::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
170+
LegacyTestServiceSubscriberChild::class.'::testDefinition3' => new ServiceClosureArgument(new TypedReference(TestDefinition3::class, TestDefinition3::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
171+
LegacyTestServiceSubscriberParent::class.'::testDefinition1' => new ServiceClosureArgument(new TypedReference(TestDefinition1::class, TestDefinition1::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
166172
];
167173

168174
$this->assertEquals($expected, $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0));
169175
}
170176

177+
/**
178+
* @group legacy
179+
*/
171180
public function testServiceSubscriberTraitWithGetter()
172181
{
173182
$container = new ContainerBuilder();
@@ -195,6 +204,108 @@ public function getFoo(): \stdClass
195204
$this->assertEquals($expected, $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0));
196205
}
197206

207+
/**
208+
* @requires PHP 8
209+
*/
210+
public function testServiceSubscriberTraitWithSubscribedServiceAttribute()
211+
{
212+
if (!class_exists(SubscribedService::class)) {
213+
$this->markTestSkipped('SubscribedService attribute not available.');
214+
}
215+
216+
$container = new ContainerBuilder();
217+
218+
$container->register('foo', TestServiceSubscriberChild::class)
219+
->addMethodCall('setContainer', [new Reference(PsrContainerInterface::class)])
220+
->addTag('container.service_subscriber')
221+
;
222+
223+
(new RegisterServiceSubscribersPass())->process($container);
224+
(new ResolveServiceSubscribersPass())->process($container);
225+
226+
$foo = $container->getDefinition('foo');
227+
$locator = $container->getDefinition((string) $foo->getMethodCalls()[0][1][0]);
228+
229+
$expected = [
230+
TestServiceSubscriberChild::class.'::invalidDefinition' => new ServiceClosureArgument(new TypedReference('Symfony\Component\DependencyInjection\Tests\Fixtures\InvalidDefinition', 'Symfony\Component\DependencyInjection\Tests\Fixtures\InvalidDefinition')),
231+
TestServiceSubscriberChild::class.'::testDefinition2' => new ServiceClosureArgument(new TypedReference(TestDefinition2::class, TestDefinition2::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
232+
TestServiceSubscriberChild::class.'::testDefinition4' => new ServiceClosureArgument(new TypedReference(TestDefinition3::class, TestDefinition3::class)),
233+
TestServiceSubscriberParent::class.'::testDefinition1' => new ServiceClosureArgument(new TypedReference(TestDefinition1::class, TestDefinition1::class)),
234+
'custom_name' => new ServiceClosureArgument(new TypedReference(TestDefinition3::class, TestDefinition3::class, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, 'custom_name')),
235+
];
236+
237+
$this->assertEquals($expected, $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0));
238+
}
239+
240+
/**
241+
* @requires PHP 8
242+
*/
243+
public function testServiceSubscriberTraitWithSubscribedServiceAttributeOnStaticMethod()
244+
{
245+
if (!class_exists(SubscribedService::class)) {
246+
$this->markTestSkipped('SubscribedService attribute not available.');
247+
}
248+
249+
$subscriber = new class() implements ServiceSubscriberInterface {
250+
use ServiceSubscriberTrait;
251+
252+
#[SubscribedService]
253+
public static function method(): TestDefinition1
254+
{
255+
}
256+
};
257+
258+
$this->expectException(\LogicException::class);
259+
260+
$subscriber::getSubscribedServices();
261+
}
262+
263+
/**
264+
* @requires PHP 8
265+
*/
266+
public function testServiceSubscriberTraitWithSubscribedServiceAttributeOnMethodWithRequiredParameters()
267+
{
268+
if (!class_exists(SubscribedService::class)) {
269+
$this->markTestSkipped('SubscribedService attribute not available.');
270+
}
271+
272+
$subscriber = new class() implements ServiceSubscriberInterface {
273+
use ServiceSubscriberTrait;
274+
275+
#[SubscribedService]
276+
public function method($param1, $param2 = null): TestDefinition1
277+
{
278+
}
279+
};
280+
281+
$this->expectException(\LogicException::class);
282+
283+
$subscriber::getSubscribedServices();
284+
}
285+
286+
/**
287+
* @requires PHP 8
288+
*/
289+
public function testServiceSubscriberTraitWithSubscribedServiceAttributeOnMethodMissingReturnType()
290+
{
291+
if (!class_exists(SubscribedService::class)) {
292+
$this->markTestSkipped('SubscribedService attribute not available.');
293+
}
294+
295+
$subscriber = new class() implements ServiceSubscriberInterface {
296+
use ServiceSubscriberTrait;
297+
298+
#[SubscribedService]
299+
public function method()
300+
{
301+
}
302+
};
303+
304+
$this->expectException(\LogicException::class);
305+
306+
$subscriber::getSubscribedServices();
307+
}
308+
198309
public function testServiceSubscriberWithSemanticId()
199310
{
200311
$container = new ContainerBuilder();
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
4+
5+
use Symfony\Contracts\Service\ServiceSubscriberTrait;
6+
7+
class LegacyTestServiceSubscriberChild extends LegacyTestServiceSubscriberParent
8+
{
9+
use ServiceSubscriberTrait;
10+
use LegacyTestServiceSubscriberTrait;
11+
12+
private function testDefinition2(): TestDefinition2
13+
{
14+
return $this->container->get(__METHOD__);
15+
}
16+
17+
private function invalidDefinition(): InvalidDefinition
18+
{
19+
return $this->container->get(__METHOD__);
20+
}
21+
22+
private function privateFunction1(): string
23+
{
24+
}
25+
26+
private function privateFunction2(): string
27+
{
28+
}
29+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
4+
5+
use Symfony\Contracts\Service\ServiceSubscriberInterface;
6+
use Symfony\Contracts\Service\ServiceSubscriberTrait;
7+
8+
class LegacyTestServiceSubscriberParent implements ServiceSubscriberInterface
9+
{
10+
use ServiceSubscriberTrait;
11+
12+
private function testDefinition1(): TestDefinition1
13+
{
14+
return $this->container->get(__METHOD__);
15+
}
16+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
4+
5+
trait LegacyTestServiceSubscriberTrait
6+
{
7+
private function testDefinition3(): TestDefinition3
8+
{
9+
return $this->container->get(__CLASS__.'::'.__FUNCTION__);
10+
}
11+
}

src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberChild.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,27 @@
22

33
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
44

5+
use Symfony\Contracts\Service\Attribute\SubscribedService;
56
use Symfony\Contracts\Service\ServiceSubscriberTrait;
67

78
class TestServiceSubscriberChild extends TestServiceSubscriberParent
89
{
910
use ServiceSubscriberTrait;
1011
use TestServiceSubscriberTrait;
1112

12-
private function testDefinition2(): TestDefinition2
13+
#[SubscribedService]
14+
private function testDefinition2(): ?TestDefinition2
1315
{
1416
return $this->container->get(__METHOD__);
1517
}
1618

19+
#[SubscribedService('custom_name')]
20+
private function testDefinition3(): TestDefinition3
21+
{
22+
return $this->container->get('custom_name');
23+
}
24+
25+
#[SubscribedService]
1726
private function invalidDefinition(): InvalidDefinition
1827
{
1928
return $this->container->get(__METHOD__);
@@ -26,4 +35,8 @@ private function privateFunction1(): string
2635
private function privateFunction2(): string
2736
{
2837
}
38+
39+
private function privateFunction3(): AnotherClass
40+
{
41+
}
2942
}

src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberParent.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,19 @@
22

33
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
44

5+
use Symfony\Contracts\Service\Attribute\SubscribedService;
56
use Symfony\Contracts\Service\ServiceSubscriberInterface;
67
use Symfony\Contracts\Service\ServiceSubscriberTrait;
78

89
class TestServiceSubscriberParent implements ServiceSubscriberInterface
910
{
1011
use ServiceSubscriberTrait;
1112

13+
public function publicFunction1(): SomeClass
14+
{
15+
}
16+
17+
#[SubscribedService]
1218
private function testDefinition1(): TestDefinition1
1319
{
1420
return $this->container->get(__METHOD__);

src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberTrait.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,16 @@
22

33
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
44

5+
use Symfony\Contracts\Service\Attribute\SubscribedService;
6+
57
trait TestServiceSubscriberTrait
68
{
7-
private function testDefinition3(): TestDefinition3
9+
protected function protectedFunction1(): SomeClass
10+
{
11+
}
12+
13+
#[SubscribedService]
14+
private function testDefinition4(): TestDefinition3
815
{
916
return $this->container->get(__CLASS__.'::'.__FUNCTION__);
1017
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Contracts\Service\Attribute;
13+
14+
use Symfony\Contracts\Service\ServiceSubscriberTrait;
15+
16+
/**
17+
* Use with {@see ServiceSubscriberTrait} to mark a method's return type
18+
* as a subscribed service.
19+
*
20+
* @author Kevin Bond <[email protected]>
21+
*/
22+
#[\Attribute(\Attribute::TARGET_METHOD)]
23+
final class SubscribedService
24+
{
25+
/**
26+
* @param string|null $key The key to use for the service
27+
* If null, use "ClassName::methodName"
28+
*/
29+
public function __construct(
30+
public ?string $key = null
31+
) {
32+
}
33+
}

0 commit comments

Comments
 (0)