Skip to content

Commit 02c1ca3

Browse files
committed
[DependencyInjection] Configure service tags via attributes.
1 parent 76f02fd commit 02c1ca3

File tree

32 files changed

+571
-22
lines changed

32 files changed

+571
-22
lines changed

.github/patch-types.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,12 @@
2828
case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/ParentNotExists.php'):
2929
case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Preload/'):
3030
case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/BadClasses/MissingParent.php'):
31+
case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TaggedService'):
3132
case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/WitherStaticReturnType.php'):
3233
case false !== strpos($file, '/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/'):
3334
case false !== strpos($file, '/src/Symfony/Component/ErrorHandler/Tests/Fixtures/'):
35+
case false !== strpos($file, '/src/Symfony/Component/EventDispatcher/Attribute/EventListener.php'):
36+
case false !== strpos($file, '/src/Symfony/Component/HttpKernel/Attribute/Reset.php'):
3437
case false !== strpos($file, '/src/Symfony/Component/HttpKernel/Tests/Fixtures/Controller/AttributeController.php'):
3538
case false !== strpos($file, '/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php'):
3639
case false !== strpos($file, '/src/Symfony/Component/PropertyInfo/Tests/Fixtures/ParentDummy.php'):
@@ -44,6 +47,7 @@
4447
case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/NotLoadableClass.php'):
4548
case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/Php74.php') && \PHP_VERSION_ID < 70400:
4649
case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/RepeatableAttribute.php'):
50+
case false !== strpos($file, '/src/Symfony/Contracts/Service/Attribute/Tag.php'):
4751
continue 2;
4852
}
4953

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Component\DependencyInjection\Compiler;
13+
14+
use Symfony\Component\DependencyInjection\ContainerBuilder;
15+
use Symfony\Contracts\Service\Attribute\TagInterface;
16+
17+
final class AttributeAutoconfigurationPass implements CompilerPassInterface
18+
{
19+
public function process(ContainerBuilder $container): void
20+
{
21+
if (80000 > \PHP_VERSION_ID || !interface_exists(TagInterface::class)) {
22+
return;
23+
}
24+
25+
foreach ($container->getDefinitions() as $definition) {
26+
if (!$definition->isAutoconfigured()) {
27+
continue;
28+
}
29+
30+
if (!$class = $container->getParameterBag()->resolveValue($definition->getClass())) {
31+
continue;
32+
}
33+
34+
try {
35+
$reflector = new \ReflectionClass($class);
36+
} catch (\ReflectionException $e) {
37+
continue;
38+
}
39+
40+
foreach ($reflector->getAttributes(TagInterface::class, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
41+
/** @var TagInterface $tag */
42+
$tag = $attribute->newInstance();
43+
$definition->addTag($tag->getName(), $tag->getAttributes());
44+
}
45+
}
46+
}
47+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public function __construct()
4242
$this->beforeOptimizationPasses = [
4343
100 => [
4444
new ResolveClassPass(),
45+
new AttributeAutoconfigurationPass(),
4546
new ResolveInstanceofConditionalsPass(),
4647
new RegisterEnvVarProcessorsPass(),
4748
],

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\DependencyInjection\Alias;
1717
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
1818
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
19+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1920
use Symfony\Component\DependencyInjection\ContainerBuilder;
2021
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
2122
use Symfony\Component\DependencyInjection\Reference;
@@ -24,6 +25,9 @@
2425
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooBarTaggedClass;
2526
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooBarTaggedForDefaultPriorityClass;
2627
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooTagClass;
28+
use Symfony\Component\DependencyInjection\Tests\Fixtures\TaggedService1;
29+
use Symfony\Component\DependencyInjection\Tests\Fixtures\TaggedService2;
30+
use Symfony\Component\DependencyInjection\Tests\Fixtures\TaggedService3;
2731
use Symfony\Contracts\Service\ServiceProviderInterface;
2832
use Symfony\Contracts\Service\ServiceSubscriberInterface;
2933

@@ -506,6 +510,41 @@ public function testTaggedServiceLocatorWithDefaultIndex()
506510
];
507511
$this->assertSame($expected, ['baz' => $serviceLocator->get('baz')]);
508512
}
513+
514+
/**
515+
* @requires PHP 8
516+
*/
517+
public function testTagsViaAttribute()
518+
{
519+
$container = new ContainerBuilder();
520+
$container->register('one', TaggedService1::class)
521+
->setPublic(true)
522+
->setAutoconfigured(true);
523+
$container->register('two', TaggedService2::class)
524+
->setPublic(true)
525+
->setAutoconfigured(true);
526+
$container->register('three', TaggedService3::class)
527+
->setPublic(true)
528+
->setAutoconfigured(true);
529+
530+
$collector = new TagCollector();
531+
$container->addCompilerPass($collector);
532+
533+
$container->compile();
534+
535+
self::assertSame([
536+
'one' => [
537+
['foo' => 'bar', 'priority' => 0],
538+
['bar' => 'baz', 'priority' => 0],
539+
],
540+
'two' => [
541+
['someAttribute' => 'prio 100', 'priority' => 100],
542+
],
543+
'three' => [
544+
['someAttribute' => 'custom_tag_class'],
545+
],
546+
], $collector->collectedTags);
547+
}
509548
}
510549

511550
class ServiceSubscriberStub implements ServiceSubscriberInterface
@@ -566,3 +605,13 @@ public function setSunshine($type)
566605
{
567606
}
568607
}
608+
609+
final class TagCollector implements CompilerPassInterface
610+
{
611+
public $collectedTags;
612+
613+
public function process(ContainerBuilder $container): void
614+
{
615+
$this->collectedTags = $container->findTaggedServiceIds('app.custom_tag');
616+
}
617+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Component\DependencyInjection\Tests\Fixtures\Attribute;
13+
14+
use Symfony\Contracts\Service\Attribute\TagInterface;
15+
16+
#[\Attribute(\Attribute::TARGET_CLASS)]
17+
final class CustomTag implements TagInterface
18+
{
19+
public function getName(): string
20+
{
21+
return 'app.custom_tag';
22+
}
23+
24+
public function getAttributes(): array
25+
{
26+
return ['someAttribute' => 'custom_tag_class'];
27+
}
28+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Component\DependencyInjection\Tests\Fixtures;
13+
14+
use Symfony\Contracts\Service\Attribute\Tag;
15+
16+
#[Tag(name: 'app.custom_tag', attributes: ['foo' => 'bar'])]
17+
#[Tag(name: 'app.custom_tag', attributes: ['bar' => 'baz'])]
18+
final class TaggedService1
19+
{
20+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Component\DependencyInjection\Tests\Fixtures;
13+
14+
use Symfony\Contracts\Service\Attribute\Tag;
15+
16+
#[Tag(name: 'app.custom_tag', priority: 100, attributes: ['someAttribute' => 'prio 100'])]
17+
final class TaggedService2
18+
{
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Component\DependencyInjection\Tests\Fixtures;
13+
14+
use Symfony\Component\DependencyInjection\Tests\Fixtures\Attribute\CustomTag;
15+
16+
#[CustomTag]
17+
final class TaggedService3
18+
{
19+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Component\EventDispatcher\Attribute;
13+
14+
use Symfony\Contracts\Service\Attribute\TagInterface;
15+
16+
/**
17+
* Service tag to autoconfigure event listeners.
18+
*
19+
* @author Alexander M. Turek <me@derrabus.de>
20+
*/
21+
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)]
22+
class EventListener implements TagInterface
23+
{
24+
public function __construct(
25+
private ?string $event = null,
26+
private ?string $method = null,
27+
private int $priority = 0
28+
) {
29+
}
30+
31+
public function getName(): string
32+
{
33+
return 'kernel.event_listener';
34+
}
35+
36+
public function getAttributes(): array
37+
{
38+
return [
39+
'event' => $this->event,
40+
'method' => $this->method,
41+
'priority' => $this->priority,
42+
];
43+
}
44+
}

src/Symfony/Component/EventDispatcher/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
5.3.0
5+
-----
6+
7+
* Added the `EventListener` service tag attribute for PHP 8.
8+
49
5.1.0
510
-----
611

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