Skip to content

Commit 55ac8dd

Browse files
remove unused use statements
1 parent 0f8f90e commit 55ac8dd

File tree

8 files changed

+427
-4
lines changed

8 files changed

+427
-4
lines changed

Annotation/DependencyInjection.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
4+
namespace SimonMarx\Symfony\Bundles\ServiceAnnotations\Annotation;
5+
6+
7+
use Doctrine\Common\Annotations\Annotation\Target;
8+
use RuntimeException;
9+
10+
/**
11+
* @Annotation
12+
* @Target({"METHOD", "PROPERTY"})
13+
*/
14+
class DependencyInjection
15+
{
16+
private ?string $serviceId = null;
17+
private ?string $target = null;
18+
private ?string $tagged = null;
19+
20+
public function __construct(array $values = [])
21+
{
22+
$this->serviceId = $values['serviceId'] ?? $values['value'] ?? null;
23+
$this->target = $values['target'] ?? null;
24+
$this->tagged = $values['tagged'] ?? null;
25+
}
26+
27+
public function setServiceId(string $serviceId): void
28+
{
29+
if (null !== $this->serviceId) {
30+
throw new RuntimeException('Cannot redefine "$serviceId"');
31+
}
32+
33+
$this->serviceId = $serviceId;
34+
}
35+
36+
public function getServiceId(): ?string
37+
{
38+
return $this->serviceId;
39+
}
40+
41+
public function setTarget(string $target): void
42+
{
43+
if (null !== $this->target) {
44+
throw new RuntimeException('Cannot redefine "$target"');
45+
}
46+
47+
$this->target = $target;
48+
}
49+
50+
public function getTarget(): ?string
51+
{
52+
return $this->target;
53+
}
54+
55+
public function getTagged(): ?string
56+
{
57+
return $this->tagged;
58+
}
59+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
4+
namespace SimonMarx\Symfony\Bundles\ServiceAnnotations\ArgumentResolver;
5+
6+
7+
use Doctrine\Common\Annotations\AnnotationReader;
8+
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Annotation\DependencyInjection;
9+
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Exception\RuntimeException;
10+
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Utils\ReflectionCacheTrait;
11+
use Symfony\Component\DependencyInjection\ContainerInterface;
12+
use Symfony\Component\HttpFoundation\Request;
13+
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
14+
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
15+
16+
/**
17+
* @TODO: Idea of Value Resolver for annotated controllers
18+
*/
19+
class DependencyInjectionAnnotationValueResolver implements ArgumentValueResolverInterface
20+
{
21+
use ReflectionCacheTrait;
22+
23+
private AnnotationReader $annotationReader;
24+
/**
25+
* @var ContainerInterface
26+
*/
27+
private ContainerInterface $container;
28+
29+
public function __construct(ContainerInterface $container)
30+
{
31+
$this->annotationReader = new AnnotationReader();
32+
$this->container = $container;
33+
}
34+
35+
public function supports(Request $request, ArgumentMetadata $argument)
36+
{
37+
return false;
38+
$annotated = $this->getAnnotationForArgument($argument, $request);
39+
40+
return null !== $annotated;
41+
}
42+
43+
public function resolve(Request $request, ArgumentMetadata $argument)
44+
{
45+
$annotation = $this->getAnnotationForArgument($argument, $request);
46+
47+
if (null === $annotation) {
48+
throw new RuntimeException('Cant find annotation for argument "%s"', $argument->getName());
49+
}
50+
51+
if (null !== $annotation->getServiceId()) {
52+
if ($this->container->has($annotation->getServiceId())) {
53+
54+
}
55+
} elseif (null !== $annotation->getTagged()) {
56+
57+
}
58+
}
59+
60+
private function getAnnotationForArgument(ArgumentMetadata $argument, Request $request): ?DependencyInjection
61+
{
62+
list($controller, $action) = $this->getController($request);
63+
64+
$rc = $this->getClassReflection($controller);
65+
66+
if (null === $rc || false === $rc->hasMethod($action)) {
67+
return null;
68+
}
69+
70+
$method = $rc->getMethod($action);
71+
72+
$annotations = \array_filter(
73+
$this->annotationReader->getMethodAnnotations($method),
74+
fn(object $annotation) => $annotation instanceof DependencyInjection && $annotation->getTarget() === $argument->getName()
75+
);
76+
77+
return array_values($annotations)[0] ?? null;
78+
}
79+
80+
private function getController(Request $request): array
81+
{
82+
$controllerString = $request->get('_controller');
83+
$parts = \explode('::', $controllerString);
84+
85+
if (\count($parts) === 1) {
86+
$parts[] = '__invoke';
87+
}
88+
89+
return $parts;
90+
}
91+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
4+
namespace SimonMarx\Symfony\Bundles\ServiceAnnotations\DependencyInjection\Compiler;
5+
6+
7+
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Annotation\DependencyInjection;
8+
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Exception\InvalidServiceAnnotationException;
9+
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Struct\ClassAnnotationScan;
10+
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Utils\CompilerPassServiceAnnotationTrait;
11+
use SimonMarx\Symfony\Bundles\ServiceAnnotations\Utils\ReflectionCacheTrait;
12+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
13+
use Symfony\Component\DependencyInjection\ContainerBuilder;
14+
use Symfony\Component\DependencyInjection\Definition;
15+
use Symfony\Component\DependencyInjection\Reference;
16+
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
17+
use function Symfony\Component\DependencyInjection\Loader\Configurator\tagged_iterator;
18+
19+
class DependencyInjectionPass implements CompilerPassInterface
20+
{
21+
use CompilerPassServiceAnnotationTrait;
22+
use ReflectionCacheTrait;
23+
24+
public function process(ContainerBuilder $container)
25+
{
26+
foreach ($container->getDefinitions() as $definition) {
27+
if (false === $this->definitionHasValidClass($definition)) {
28+
continue;
29+
}
30+
31+
$classAnnotationScan = $this->fullAnnotationScan($definition);
32+
33+
$this->handleMethodAnnotations($classAnnotationScan, $definition);
34+
$this->handlePropertyAnnotations($classAnnotationScan, $definition);
35+
}
36+
}
37+
38+
private function handlePropertyAnnotations(ClassAnnotationScan $annotationScan, Definition $definition)
39+
{
40+
/**
41+
* @var string $propertyName
42+
* @var DependencyInjection[] $propertyAnnotations
43+
*/
44+
foreach ($annotationScan->findPropertyAnnotations(DependencyInjection::class) as $propertyName => $propertyAnnotations) {
45+
$property = $this->getPropertyReflection($definition->getClass(), $propertyName);
46+
47+
if (false === $property->isPublic()) {
48+
throw new InvalidServiceAnnotationException(\sprintf('Property %s::%s cannot be configured with @DependencyInjection cause it is not a public property', $property->getDeclaringClass()->getName(), $property->getName()));
49+
}
50+
51+
if (\count($propertyAnnotations) > 1) {
52+
throw new InvalidServiceAnnotationException(\sprintf('Property %s::%s has more than one annotation of type @DependencyInjection which is not possible to inject multiple services to property', $property->getDeclaringClass()->getName(), $property->getName()));
53+
}
54+
55+
$propertyAnnotation = $propertyAnnotations[0];
56+
57+
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'));
59+
}
60+
61+
62+
if (null !== $propertyAnnotation->getServiceId()) {
63+
$definition->setProperty($property->getName(), service($propertyAnnotation->getServiceId()));
64+
} elseif (null !== $propertyAnnotation->getTagged()) {
65+
$definition->setProperty($property->getName(), tagged_iterator($propertyAnnotation->getTagged()));
66+
}
67+
}
68+
}
69+
70+
private function handleMethodAnnotations(ClassAnnotationScan $annotationScan, Definition $definition)
71+
{
72+
/**
73+
* @var string $methodName
74+
* @var DependencyInjection[] $methodAnnotations
75+
*/
76+
foreach ($annotationScan->findMethodAnnotations(DependencyInjection::class) as $methodName => $methodAnnotations) {
77+
$method = $this->getMethodReflection($definition->getClass(), $methodName);
78+
79+
if (false === $method->isConstructor()) {
80+
// Currently only constructor is supported, @TODO: Add setter injection support
81+
continue;
82+
}
83+
84+
$targetless = \array_filter($methodAnnotations, fn(DependencyInjection $dependencyInjection) => $dependencyInjection->getTarget() === null);
85+
86+
if (\count($targetless) > 0 && $method->getNumberOfParameters() > 1) {
87+
throw new InvalidServiceAnnotationException(\sprintf('Method %s::%s has a @DependencyInjection annotation without a target defined, this is only possible for methods with one parameter or properties', $method->getDeclaringClass()->getName(), $method->getName()));
88+
}
89+
90+
foreach ($method->getParameters() as $parameter) {
91+
$foundTargetAnnotation = \array_values(\array_filter(
92+
$methodAnnotations,
93+
fn(DependencyInjection $dependencyInjection) => $dependencyInjection->getTarget() === $parameter->getName()
94+
))[0] ?? null;
95+
96+
if ($foundTargetAnnotation instanceof DependencyInjection) {
97+
if (null !== $foundTargetAnnotation->getServiceId()) {
98+
$definition->setArgument(\sprintf('$%s', $parameter->getName()), new Reference($foundTargetAnnotation->getServiceId()));
99+
} elseif (null !== $foundTargetAnnotation->getTagged()) {
100+
$definition->setArgument(\sprintf('$%s', $parameter->getName()), tagged_iterator($foundTargetAnnotation->getTagged()));
101+
}
102+
}
103+
}
104+
}
105+
}
106+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
4+
namespace SimonMarx\Symfony\Bundles\ServiceAnnotations\DependencyInjection;
5+
6+
7+
use Symfony\Component\Config\FileLocator;
8+
use Symfony\Component\DependencyInjection\ContainerBuilder;
9+
use Symfony\Component\DependencyInjection\Extension\Extension;
10+
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
11+
12+
class SimaServiceAnnotationsExtension extends Extension
13+
{
14+
public function load(array $configs, ContainerBuilder $container)
15+
{
16+
$loader = new YamlFileLoader(
17+
$container,
18+
new FileLocator(__DIR__.'/../Resources/config')
19+
);
20+
}
21+
}

Exception/RuntimeException.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
4+
namespace SimonMarx\Symfony\Bundles\ServiceAnnotations\Exception;
5+
6+
7+
use Exception;
8+
9+
class RuntimeException extends Exception
10+
{
11+
12+
}

Resources/config/services.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
services:
2+
SimonMarx\Symfony\Bundles\ServiceAnnotations\ArgumentResolver\DependencyInjectionAnnotationValueResolver:
3+
arguments:
4+
$container: '@Symfony\Component\DependencyInjection\ContainerInterface'
5+
tags:
6+
- { name: 'controller.argument_value_resolver', priority: 1000 }

0 commit comments

Comments
 (0)