Skip to content

Commit 147fbff

Browse files
author
christianblos
committed
allow to modify container by method annotations
1 parent 70e2787 commit 147fbff

File tree

12 files changed

+213
-36
lines changed

12 files changed

+213
-36
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace Example\ModifyContainerFromAnnotation\Annotation;
4+
5+
use Example\ModifyContainerFromAnnotation\EventDispatcher;
6+
use Symfony\Component\DependencyInjection\Annotation\Modifier\ModifyContainerInterface;
7+
use Symfony\Component\DependencyInjection\Annotation\Service;
8+
use Symfony\Component\DependencyInjection\ContainerBuilder;
9+
use Symfony\Component\DependencyInjection\Definition;
10+
11+
/**
12+
* @Annotation
13+
*/
14+
class ListenTo implements ModifyContainerInterface
15+
{
16+
/**
17+
* @var string
18+
*/
19+
public $value;
20+
21+
/**
22+
* {@inheritdoc}
23+
*/
24+
public function modifyContainer(
25+
$serviceId,
26+
Service $service,
27+
Definition $definition,
28+
$methodName,
29+
ContainerBuilder $container
30+
) {
31+
$dispatcher = $container->getDefinition(EventDispatcher::class);
32+
$dispatcher->addMethodCall('addListener', [$this->value, $serviceId . '::' . $methodName]);
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Example\ModifyContainerFromAnnotation;
4+
5+
use Symfony\Component\DependencyInjection\Annotation\Service;
6+
7+
/**
8+
* @Service(public=true)
9+
*/
10+
class EventDispatcher
11+
{
12+
private $result = '';
13+
14+
public function addListener($event, $method)
15+
{
16+
$this->result .= $event . ' -> ' . $method;
17+
}
18+
19+
public function get()
20+
{
21+
return $this->result;
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Example\ModifyContainerFromAnnotation;
4+
5+
use Example\ModifyContainerFromAnnotation\Annotation\ListenTo;
6+
use Symfony\Component\DependencyInjection\Annotation\Service;
7+
8+
/**
9+
* @Service(public=true, id="myService")
10+
*/
11+
class TestService
12+
{
13+
/**
14+
* @ListenTo("someEvent")
15+
*/
16+
public function doSomethingWhenEventHappens()
17+
{
18+
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
use Symfony\Component\DependencyInjection\Annotation\Compiler\AnnotationPass;
4+
use Symfony\Component\DependencyInjection\ContainerBuilder;
5+
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
6+
7+
require_once __DIR__ . '/../../vendor/autoload.php';
8+
9+
$srcDirs = [
10+
__DIR__ . '/classes',
11+
];
12+
13+
$containerBuilder = new ContainerBuilder();
14+
$containerBuilder->addCompilerPass(AnnotationPass::createDefault($srcDirs));
15+
$containerBuilder->compile();
16+
17+
$dumper = new PhpDumper($containerBuilder);
18+
$dumpContent = $dumper->dump(['class' => 'DumpedServiceContainer']);
19+
20+
file_put_contents(__DIR__ . '/dumped_container.php', $dumpContent);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
/** @var ContainerInterface $container */
3+
4+
use Example\ModifyContainerFromAnnotation\EventDispatcher;
5+
use Psr\Container\ContainerInterface;
6+
7+
require_once __DIR__ . '/../../vendor/autoload.php';
8+
require_once __DIR__ . '/dumped_container.php';
9+
$container = new DumpedServiceContainer();
10+
11+
/** @var EventDispatcher $dispatcher */
12+
$dispatcher = $container->get(EventDispatcher::class);
13+
14+
if ($dispatcher->get() === 'someEvent -> myService::doSomethingWhenEventHappens') {
15+
echo 'yes';
16+
}

examples/own_inject_implementation/classes/Annotation/Inject.php

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,29 @@
22

33
namespace Example\OwnInjectImplementation\Annotation;
44

5-
use Symfony\Component\DependencyInjection\Annotation\Inject\MethodAnnotationInterface;
5+
use Symfony\Component\DependencyInjection\Annotation\Modifier\ModifyServiceAnnotationInterface;
66
use Symfony\Component\DependencyInjection\Annotation\Service;
77
use Symfony\Component\DependencyInjection\ContainerBuilder;
88

99
/**
1010
* @Annotation
1111
*/
12-
class Inject implements MethodAnnotationInterface
12+
class Inject implements ModifyServiceAnnotationInterface
1313
{
1414
/**
1515
* @var array
1616
*/
1717
public $value;
1818

1919
/**
20+
* @param string $serviceId
2021
* @param Service $service
2122
* @param string $methodName
2223
* @param ContainerBuilder $container
2324
*
2425
* @return Service
2526
*/
26-
public function modifyService(Service $service, $methodName, ContainerBuilder $container)
27+
public function modifyService($serviceId, Service $service, $methodName, ContainerBuilder $container)
2728
{
2829
$injects = $this->getMethodInjects($service, $methodName);
2930

src/Compiler/AnnotationPass.php

+33-6
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
use ReflectionParameter;
88
use Symfony\Component\Config\Resource\DirectoryResource;
99
use Symfony\Component\DependencyInjection\Annotation\Inject\InjectableInterface;
10-
use Symfony\Component\DependencyInjection\Annotation\Inject\MethodAnnotationInterface;
10+
use Symfony\Component\DependencyInjection\Annotation\Modifier\ModifyContainerInterface;
11+
use Symfony\Component\DependencyInjection\Annotation\Modifier\ModifyServiceAnnotationInterface;
1112
use Symfony\Component\DependencyInjection\Annotation\Service;
1213
use Symfony\Component\DependencyInjection\Annotation\Tag\TagInterface;
1314
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
@@ -67,23 +68,26 @@ public function process(ContainerBuilder $container)
6768

6869
$services = $this->serviceFinder->findServiceAnnotations($this->srcDirs);
6970

71+
// register all services first so they are known on the further steps
7072
foreach ($services as $id => $service) {
71-
$service = $this->modifyServiceByMethodAnnotations($service, $container);
73+
$service = $this->modifyServiceByMethodAnnotations($id, $service, $container);
7274

7375
$container->setDefinition($id, $this->createServiceDefinition($service));
7476
}
7577

76-
// resolve arguments after all services are added, so we know all available services here
7778
foreach ($services as $id => $service) {
7879
$definition = $container->getDefinition($id);
7980

81+
// resolve arguments and method calls
8082
if (!$service->factoryClass) {
8183
$definition->setArguments($this->getConstructorArguments($service, $container));
8284

8385
foreach ($service->methodCalls as $methodCall) {
8486
$this->addMethodCall($definition, $methodCall, $service, $container);
8587
}
8688
}
89+
90+
$this->modifyContainerByMethodAnnotations($id, $service, $definition, $container);
8791
}
8892
}
8993

@@ -120,23 +124,46 @@ protected function createServiceDefinition(Service $service)
120124
}
121125

122126
/**
127+
* @param string $serviceId
123128
* @param Service $service
124129
* @param ContainerBuilder $container
125130
*
126131
* @return Service
127132
*/
128-
private function modifyServiceByMethodAnnotations(Service $service, ContainerBuilder $container)
133+
private function modifyServiceByMethodAnnotations($serviceId, Service $service, ContainerBuilder $container)
129134
{
130135
foreach ($service->getAllMethodAnnotations() as $method => $methodAnnotations) {
131-
/** @var MethodAnnotationInterface[] $methodAnnotations */
132136
foreach ($methodAnnotations as $methodAnnotation) {
133-
$service = $methodAnnotation->modifyService($service, $method, $container);
137+
if ($methodAnnotation instanceof ModifyServiceAnnotationInterface) {
138+
$service = $methodAnnotation->modifyService($serviceId, $service, $method, $container);
139+
}
134140
}
135141
}
136142

137143
return $service;
138144
}
139145

146+
/**
147+
* @param string $serviceId
148+
* @param Service $service
149+
* @param Definition $definition
150+
* @param ContainerBuilder $container
151+
*/
152+
private function modifyContainerByMethodAnnotations(
153+
$serviceId,
154+
Service $service,
155+
Definition $definition,
156+
ContainerBuilder $container
157+
) {
158+
foreach ($service->getAllMethodAnnotations() as $method => $methodAnnotations) {
159+
foreach ($methodAnnotations as $methodAnnotation) {
160+
if ($methodAnnotation instanceof ModifyContainerInterface) {
161+
$methodAnnotation->modifyContainer($serviceId, $service, $definition, $method, $container);
162+
}
163+
}
164+
}
165+
}
166+
140167
/**
141168
* @param Definition $definition
142169
* @param Service $service

src/Compiler/ServiceFinder.php

+2-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
use Doctrine\Common\Annotations\Reader;
66
use ReflectionClass;
7-
use Symfony\Component\DependencyInjection\Annotation\Inject\MethodAnnotationInterface;
87
use Symfony\Component\DependencyInjection\Annotation\Service;
98

109
class ServiceFinder
@@ -81,7 +80,7 @@ public function findServiceAnnotations(array $dirs)
8180
/**
8281
* @param ReflectionClass $refClass
8382
*
84-
* @return MethodAnnotationInterface[][]
83+
* @return array
8584
*/
8685
private function getMethodAnnotations(ReflectionClass $refClass)
8786
{
@@ -94,9 +93,7 @@ private function getMethodAnnotations(ReflectionClass $refClass)
9493

9594
$methodAnnotations = $this->annotationReader->getMethodAnnotations($method);
9695
foreach ($methodAnnotations as $methodAnnotation) {
97-
if ($methodAnnotation instanceof MethodAnnotationInterface) {
98-
$annotations[$method->getName()][] = $methodAnnotation;
99-
}
96+
$annotations[$method->getName()][] = $methodAnnotation;
10097
}
10198
}
10299

src/Inject/MethodAnnotationInterface.php

-18
This file was deleted.
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Annotation\Modifier;
4+
5+
use Symfony\Component\DependencyInjection\Annotation\Service;
6+
use Symfony\Component\DependencyInjection\ContainerBuilder;
7+
use Symfony\Component\DependencyInjection\Definition;
8+
9+
/**
10+
* Annotation should implement this interface to modify any service in the container.
11+
*/
12+
interface ModifyContainerInterface
13+
{
14+
/**
15+
* This method is called after all services are registered.
16+
* You can modify any service in the container here :)
17+
*
18+
* @param string $serviceId The ID of the service on which this annotation is added to
19+
* @param Service $service The source Service annotation. Just if you need infos from it
20+
* @param Definition $definition The registered service definition of the class on which this annotation is
21+
* added to.
22+
* @param string $methodName The method on which this annotation is added to
23+
* @param ContainerBuilder $container The container which you can modify here
24+
*/
25+
public function modifyContainer(
26+
$serviceId,
27+
Service $service,
28+
Definition $definition,
29+
$methodName,
30+
ContainerBuilder $container
31+
);
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Annotation\Modifier;
4+
5+
use Symfony\Component\DependencyInjection\Annotation\Service;
6+
use Symfony\Component\DependencyInjection\ContainerBuilder;
7+
8+
/**
9+
* Annotation should implement this interface to modify the Service annotation object before
10+
* it's added to the container.
11+
*/
12+
interface ModifyServiceAnnotationInterface
13+
{
14+
/**
15+
* Is called before the Service is added to the container.
16+
* You should not modify the container here. It doesn't contain all services yet!
17+
*
18+
* @param string $serviceId The ID that will be used to register the service to the container
19+
* @param Service $service The Service annotation you can modify here
20+
* @param string $methodName The method on which this annotation is added to
21+
* @param ContainerBuilder $container The current container. Use it to get some config params for instance
22+
*
23+
* @return Service
24+
*/
25+
public function modifyService($serviceId, Service $service, $methodName, ContainerBuilder $container);
26+
}

src/Service.php

+3-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace Symfony\Component\DependencyInjection\Annotation;
44

55
use ReflectionClass;
6-
use Symfony\Component\DependencyInjection\Annotation\Inject\MethodAnnotationInterface;
76

87
/**
98
* @Annotation
@@ -88,15 +87,15 @@ public function getClass()
8887
}
8988

9089
/**
91-
* @param MethodAnnotationInterface[][] $annotations
90+
* @param array $annotations
9291
*/
9392
public function setMethodAnnotations(array $annotations)
9493
{
9594
$this->methodAnnotations = $annotations;
9695
}
9796

9897
/**
99-
* @return MethodAnnotationInterface[][]
98+
* @return array
10099
*/
101100
public function getAllMethodAnnotations()
102101
{
@@ -106,7 +105,7 @@ public function getAllMethodAnnotations()
106105
/**
107106
* @param string $methodName
108107
*
109-
* @return MethodAnnotationInterface[][]
108+
* @return array
110109
*/
111110
public function getMethodAnnotations($methodName)
112111
{

0 commit comments

Comments
 (0)