Skip to content

Commit 1bda826

Browse files
Add multiple service annotations
1 parent 7463fed commit 1bda826

File tree

8 files changed

+117
-59
lines changed

8 files changed

+117
-59
lines changed

Annotation/DependencyInjection.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ class DependencyInjection
1616
private ?string $serviceId = null;
1717
private ?string $target = null;
1818
private ?string $tagged = null;
19+
private ?string $parameter = null;
1920

2021
public function __construct(array $values = [])
2122
{
2223
$this->serviceId = $values['serviceId'] ?? $values['value'] ?? null;
2324
$this->target = $values['target'] ?? null;
2425
$this->tagged = $values['tagged'] ?? null;
26+
$this->parameter = $values['parameter'] ?? null;
2527
}
2628

2729
public function setServiceId(string $serviceId): void
@@ -47,6 +49,11 @@ public function setTarget(string $target): void
4749
$this->target = $target;
4850
}
4951

52+
public function getParameter(): ?string
53+
{
54+
return $this->parameter;
55+
}
56+
5057
public function getTarget(): ?string
5158
{
5259
return $this->target;

Annotation/ServiceTag.php

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,11 @@
1313
class ServiceTag
1414
{
1515
private string $name;
16-
private ?int $priority = null;
1716
private array $attributes = [];
1817

1918
public function __construct(array $values = [])
2019
{
2120
$this->name = $values['name'] ?? $values['value'];
22-
$this->priority = $values['priority'] ?? null;
2321

2422
if (\array_key_exists('value', $values)) {
2523
unset($values['value']);
@@ -35,11 +33,6 @@ public function getName(): string
3533
return $this->name;
3634
}
3735

38-
public function getPriority(): ?int
39-
{
40-
return $this->priority ?: $this->getAttributes()['priority'] ?? null;
41-
}
42-
4336
public function getAttributes(): array
4437
{
4538
return $this->attributes;

DependencyInjection/Compiler/DependencyInjectionPass.php

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@ class DependencyInjectionPass implements CompilerPassInterface
2121
use CompilerPassServiceAnnotationTrait;
2222
use ReflectionCacheTrait;
2323

24+
private ContainerBuilder $container;
25+
2426
public function process(ContainerBuilder $container)
2527
{
28+
$this->container = $container;
29+
2630
foreach ($container->getDefinitions() as $definition) {
2731
if (false === $this->definitionHasValidClass($definition)) {
2832
continue;
@@ -35,6 +39,11 @@ public function process(ContainerBuilder $container)
3539
}
3640
}
3741

42+
private function findParameter(string $name)
43+
{
44+
return $this->container->getParameter($name);
45+
}
46+
3847
private function handlePropertyAnnotations(ClassAnnotationScan $annotationScan, Definition $definition)
3948
{
4049
/**
@@ -55,15 +64,21 @@ private function handlePropertyAnnotations(ClassAnnotationScan $annotationScan,
5564
$propertyAnnotation = $propertyAnnotations[0];
5665

5766
if (null === $propertyAnnotation->getServiceId() && null === $propertyAnnotation->getTagged()) {
58-
throw new InvalidServiceAnnotationException(\sprintf('Property %s::%s has @DependencyInjection without "serviceId" and "tagged" option. One of them must be defined'));
67+
throw new InvalidServiceAnnotationException(\sprintf('Property %s::%s has @DependencyInjection without "serviceId", "tagged" or "parameter" option. One of them must be defined', $property->getDeclaringClass()->getName(), $property->getName()));
5968
}
6069

6170

6271
if (null !== $propertyAnnotation->getServiceId()) {
6372
$definition->setProperty($property->getName(), service($propertyAnnotation->getServiceId()));
64-
} elseif (null !== $propertyAnnotation->getTagged()) {
73+
}
74+
75+
if (null !== $propertyAnnotation->getTagged()) {
6576
$definition->setProperty($property->getName(), tagged_iterator($propertyAnnotation->getTagged()));
6677
}
78+
79+
if (null !== $propertyAnnotation->getParameter()) {
80+
$definition->setProperty($property->getName(), $this->findParameter($propertyAnnotation->getParameter()));
81+
}
6782
}
6883
}
6984

@@ -96,9 +111,15 @@ private function handleMethodAnnotations(ClassAnnotationScan $annotationScan, De
96111
if ($foundTargetAnnotation instanceof DependencyInjection) {
97112
if (null !== $foundTargetAnnotation->getServiceId()) {
98113
$definition->setArgument(\sprintf('$%s', $parameter->getName()), new Reference($foundTargetAnnotation->getServiceId()));
99-
} elseif (null !== $foundTargetAnnotation->getTagged()) {
114+
}
115+
116+
if (null !== $foundTargetAnnotation->getTagged()) {
100117
$definition->setArgument(\sprintf('$%s', $parameter->getName()), tagged_iterator($foundTargetAnnotation->getTagged()));
101118
}
119+
120+
if (null !== $foundTargetAnnotation->getParameter()) {
121+
$definition->setArgument(\sprintf('$%s', $parameter->getName()), $this->findParameter($foundTargetAnnotation->getParameter()));
122+
}
102123
}
103124
}
104125
}

DependencyInjection/Compiler/ServiceTagArgumentPass.php

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,39 @@
44
namespace SimonMarx\Symfony\Bundles\ServiceAnnotations\DependencyInjection\Compiler;
55

66

7+
use App\Serializer\Normalizer\DateTimeNormalizer;
78
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Annotation\ServiceTag;
89
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Annotation\ServiceTagArgument;
910
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Exception\InvalidServiceAnnotationException;
1011
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Utils\CompilerPassServiceAnnotationTrait;
12+
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Utils\InstanceOfInjectionTrait;
13+
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Utils\ObjectCloner;
14+
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Utils\ObjectClonerContext;
15+
use Symfony\Component\DependencyInjection\ChildDefinition;
1116
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1217
use Symfony\Component\DependencyInjection\ContainerBuilder;
18+
use Symfony\Component\DependencyInjection\Definition;
19+
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
1320

1421
class ServiceTagArgumentPass implements CompilerPassInterface
1522
{
1623
use CompilerPassServiceAnnotationTrait;
24+
use InstanceOfInjectionTrait;
1725

1826
public function process(ContainerBuilder $container)
1927
{
2028
foreach ($container->getDefinitions() as $serviceId => $definition) {
29+
if ($this->checkDefinitionHasInstanceOfConfigurations($container, $definition)) {
30+
continue;
31+
}
32+
2133
if (false === $this->definitionHasValidClass($definition) || false === $this->definitionHasAnnotation($definition, ServiceTagArgument::class)) {
2234
continue;
2335
}
2436

2537
/** @var ServiceTagArgument[] $arguments */
2638
$arguments = $this->getDefinitionAnnotations($definition, ServiceTagArgument::class);
27-
/** @var ServiceTag[] $serviceTags */
28-
$serviceTags = $this->getDefinitionAnnotations($definition, ServiceTag::class);
39+
$serviceTags = $definition->getTags();
2940

3041
$attributesByTag = [];
3142

@@ -34,45 +45,42 @@ public function process(ContainerBuilder $container)
3445
throw new InvalidServiceAnnotationException(\sprintf('Please define in your class "%s" in annotation "@ServiceTagArgument" the property "tag", since you have more than one "@ServiceTag" definitions in your class or parents of your class the system cannot autodetect the service tag for your arguments', $definition->getClass()));
3546
}
3647

37-
$tagName = $tagArgument->getTag() ?: $serviceTags[0]->getName();
38-
$tagArgument->setTag($tagName);
48+
$tagName = $tagArgument->getTag() ?: \array_keys($serviceTags)[0] ?? null;
3949

40-
$attributesByTag[$tagName][] = $tagArgument;
41-
}
42-
43-
$definedAttributesByTag = [];
50+
if (null === $tagName) {
51+
throw new InvalidServiceAnnotationException(\sprintf('Using annotation "%s" requires that the service definition has tags defined. Your service "%s" does not have any tag defined', ServiceTagArgument::class, $serviceId));
52+
}
4453

45-
foreach ($attributesByTag as $tag => $attributes) {
46-
foreach ($definition->getTag($tag) as $tagDefinition) {
47-
$definedAttributesByTag[$tag] = \array_replace($definedAttributesByTag[$tag] ?? [], \array_keys($tagDefinition));
54+
if (null === $tagArgument->getTag()) {
55+
$tagArgument->setTag($tagName);
4856
}
49-
}
5057

51-
$usedAttributes = [];
58+
$attributesByTag[$tagName][] = $tagArgument;
59+
}
5260

53-
/** @var ServiceTagArgument $attribute */
54-
foreach ($attributesByTag as $tag => $attributes) {
55-
foreach ($attributes as $attribute) {
56-
if (true === \in_array($attribute->getArgument(), $definedAttributesByTag[$tag] ?? []) && true === $attribute->getIgnoreWhenDefined() && true === $attribute->getExceptionWhenDefined()) {
57-
throw new InvalidServiceAnnotationException(\sprintf('Attribute "%s" defined in class "%s" was already defined for tag "%s". Add options "ignoreWhenDefined" to false to overwrite attribute or set option "exceptionWhenDefined" to false to hide the exception and ignore the attribute', $attribute->getArgument(), $definition->getClass(), $attribute->getTag()));
61+
$existingDefinitionTags = $definition->getTags();
62+
63+
/**
64+
* @var string $tag
65+
* @var ServiceTagArgument[] $serviceTagArguments
66+
*/
67+
foreach ($attributesByTag as $tag => $serviceTagArguments) {
68+
foreach ($serviceTagArguments as $serviceTagArgument) {
69+
$tagAttributes = $existingDefinitionTags[$serviceTagArgument->getTag()] ?? [];
70+
71+
if (\count($tagAttributes) > 0) {
72+
foreach ($tagAttributes as $index => $attributes) {
73+
$tagAttributes[$index][$serviceTagArgument->getArgument()] = $serviceTagArgument->getValue();
74+
}
75+
} else {
76+
$tagAttributes[0][$serviceTagArgument->getArgument()] = $serviceTagArgument->getValue();
5877
}
5978

60-
if (
61-
false === \in_array($attribute->getArgument(), $definedAttributesByTag[$tag])
62-
|| (
63-
true === \in_array($attribute->getArgument(), $definedAttributesByTag[$tag])
64-
&& false === $attribute->getIgnoreWhenDefined()
65-
)
66-
) {
67-
$usedAttributes[$tag][$attribute->getArgument()] = $attribute->getValue();
68-
}
79+
$existingDefinitionTags[$serviceTagArgument->getTag()] = $tagAttributes;
6980
}
7081
}
7182

72-
73-
foreach($usedAttributes as $tag => $attributes) {
74-
$definition->addTag($tag, $attributes);
75-
}
83+
$definition->setTags($existingDefinitionTags);
7684
}
7785
}
7886
}

DependencyInjection/Compiler/ServiceTagPass.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,46 @@
44
namespace SimonMarx\Symfony\Bundles\ServiceAnnotations\DependencyInjection\Compiler;
55

66

7+
use App\DocReader\AnnotationHandler\RouteAnnotationHandler;
8+
use App\Event\Doctrine\UserPrePersistListener;
9+
use App\Serializer\Normalizer\DateTimeNormalizer;
10+
use App\Service\DocReader\Controller\DocumentationController;
11+
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Annotation\EventListener;
712
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Annotation\ServiceTag;
813
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Utils\CompilerPassServiceAnnotationTrait;
14+
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Utils\InstanceOfInjectionTrait;
915
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Utils\ReflectionCacheTrait;
1016
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1117
use Symfony\Component\DependencyInjection\ContainerBuilder;
18+
use Symfony\Component\DependencyInjection\Definition;
1219

1320
class ServiceTagPass implements CompilerPassInterface
1421
{
1522
use ReflectionCacheTrait;
1623
use CompilerPassServiceAnnotationTrait;
24+
use InstanceOfInjectionTrait;
1725

1826
public function process(ContainerBuilder $container)
1927
{
2028
foreach ($container->getDefinitions() as $serviceId => $definition) {
21-
if (false === $this->definitionHasValidClass($definition) || false === $this->definitionHasAnnotation($definition, ServiceTag::class)) {
29+
if (
30+
false === $this->definitionHasValidClass($definition)
31+
|| (
32+
false === $this->definitionHasAnnotation($definition, ServiceTag::class)
33+
&& false === $this->definitionHasAnnotation($definition, EventListener::class)
34+
)
35+
) {
2236
continue;
2337
}
2438

2539
/** @var ServiceTag[] $tags */
2640
$tags = $this->getDefinitionAnnotations($definition, ServiceTag::class);
41+
/** @var EventListener[] $listeners */
42+
$listeners = $this->getDefinitionAnnotations($definition, EventListener::class);
43+
44+
foreach ($listeners as $listener) {
45+
$definition->addTag($listener->getEventTag(), $listener->getAttributes());
46+
}
2747

2848
foreach ($tags as $tag) {
2949
$definition->addTag($tag->getName(), $tag->getAttributes());

SimaServiceAnnotationsBundle.php

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44
namespace SimonMarx\Symfony\Bundles\ServiceAnnotations;
55

66

7+
use SimonMarx\Symfony\Bundles\ServiceAnnotations\DependencyInjection\Compiler\DefinitionManipulationPass;
78
use SimonMarx\Symfony\Bundles\ServiceAnnotations\DependencyInjection\Compiler\DependencyInjectionPass;
89
use SimonMarx\Symfony\Bundles\ServiceAnnotations\DependencyInjection\Compiler\NoServicePass;
910
use SimonMarx\Symfony\Bundles\ServiceAnnotations\DependencyInjection\Compiler\ParentServicePass;
1011
use SimonMarx\Symfony\Bundles\ServiceAnnotations\DependencyInjection\Compiler\ServiceAliasPass;
12+
use SimonMarx\Symfony\Bundles\ServiceAnnotations\DependencyInjection\Compiler\ServiceCallPass;
1113
use SimonMarx\Symfony\Bundles\ServiceAnnotations\DependencyInjection\Compiler\ServiceTagArgumentPass;
1214
use SimonMarx\Symfony\Bundles\ServiceAnnotations\DependencyInjection\Compiler\ServiceTagPass;
15+
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
1316
use Symfony\Component\DependencyInjection\ContainerBuilder;
1417
use Symfony\Component\HttpKernel\Bundle\Bundle;
1518

@@ -18,13 +21,16 @@ class SimaServiceAnnotationsBundle extends Bundle
1821
public function build(ContainerBuilder $container)
1922
{
2023
parent::build($container);
21-
2224
$container
23-
->addCompilerPass(new ServiceTagPass())
24-
->addCompilerPass(new ServiceAliasPass())
25-
->addCompilerPass(new DependencyInjectionPass())
26-
->addCompilerPass(new ServiceTagArgumentPass())
27-
->addCompilerPass(new ParentServicePass())
28-
->addCompilerPass(new NoServicePass());
25+
->addCompilerPass(new DefinitionManipulationPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 1)
26+
->addCompilerPass(new ServiceTagPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 1)
27+
->addCompilerPass(new ServiceAliasPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 1)
28+
->addCompilerPass(new DependencyInjectionPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 1)
29+
->addCompilerPass(new ServiceTagArgumentPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 1)
30+
->addCompilerPass(new ParentServicePass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 1)
31+
->addCompilerPass(new ServiceCallPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 1)
32+
33+
->addCompilerPass(new NoServicePass(), PassConfig::TYPE_REMOVE);
34+
;
2935
}
3036
}

Utils/CompilerPassServiceAnnotationTrait.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
namespace SimonMarx\Symfony\Bundles\ServiceAnnotations\Utils;
55

66

7+
use App\DocReader\AnnotationHandler\RouteAnnotationHandler;
8+
use App\DocReader\AnnotationHandler\RouteHandler;
79
use Doctrine\Common\Annotations\AnnotationReader;
810
use ReflectionClass;
911
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Annotation\IgnoreParentServiceAnnotations;
@@ -41,7 +43,6 @@ private function definitionHasAnnotation(Definition $definition, string $annotat
4143
return false;
4244
}
4345

44-
4546
foreach ($this->getDefinitionAnnotations($definition) as $annotation) {
4647
if ($annotation instanceof $annotationClass) {
4748
return true;

Utils/ReflectionCacheTrait.php

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ public function getPropertyReflections(string $className, bool $recursive = fals
4242
{
4343
$classReflection = $this->getClassReflection($className, $recursive);
4444

45+
if (null === $classReflection) {
46+
return [];
47+
}
48+
4549
$properties = $classReflection->getProperties();
4650

4751
if (false !== $classReflection->getParentClass() && true === $recursive) {
@@ -76,18 +80,16 @@ public function getMethodReflection(string $className, string $methodName): ?Ref
7680
}
7781
}
7882

79-
public function getPropertyReflection(string $className, string $propertyName): ?ReflectionProperty
83+
public function getPropertyReflection(string $className, string $propertyName, bool $recursive = false): ?ReflectionProperty
8084
{
81-
$rc = $this->getClassReflection($className);
85+
$properties = $this->getPropertyReflections($className, $propertyName, $recursive);
8286

83-
if (null === $rc) {
84-
return null;
87+
foreach ($properties as $property) {
88+
if ($property->getName() === $propertyName) {
89+
return $property;
90+
}
8591
}
8692

87-
try {
88-
return $rc->getProperty($propertyName);
89-
} catch (ReflectionException $exception) {
90-
return null;
91-
}
93+
return null;
9294
}
9395
}

0 commit comments

Comments
 (0)