Skip to content

Commit b128f0c

Browse files
authored
Add listener for gathering services (#215)
1 parent 0ed6964 commit b128f0c

5 files changed

+126
-1
lines changed

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## Unreleased Changes
9+
10+
## [v1.5.2](https://github.com/cspray/annoated-container/tree/v1.5.2) - 2022-08-13
11+
12+
### Added
13+
14+
- Added a `ServiceGatherListener` that encapsulates gathering a collection of services from the Container matching a given type. One of the primary use cases is to prepare a service that needs a collection of other services. For example, adding Controllers to a HTTP Routing system.
15+
816
## [v1.5.1](https://github.com/cspray/annotated-container/tree/v1.5.1) - 2022-08-13
917

1018
### Fixed

VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.5.1
1+
1.5.2

src/ServiceGatheringListener.php

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
namespace Cspray\AnnotatedContainer;
4+
5+
use Cspray\Typiphy\ObjectType;
6+
7+
abstract class ServiceGatheringListener implements AnnotatedContainerListener {
8+
9+
private ContainerDefinition $containerDefinition;
10+
private AnnotatedContainer $container;
11+
12+
final public function handle(AnnotatedContainerEvent $event) : void {
13+
if ($event->getLifecycle() === AnnotatedContainerLifecycle::BeforeContainerCreation) {
14+
$containerDefinition = $event->getTarget();
15+
assert($containerDefinition instanceof ContainerDefinition);
16+
$this->containerDefinition = $containerDefinition;
17+
} else if ($event->getLifecycle() === AnnotatedContainerLifecycle::AfterContainerCreation) {
18+
$container = $event->getTarget();
19+
assert($container instanceof AnnotatedContainer);
20+
$this->container = $container;
21+
$this->doServiceGathering();
22+
}
23+
}
24+
25+
final protected function getServicesOfType(ObjectType $objectType) : \Generator {
26+
foreach ($this->containerDefinition->getServiceDefinitions() as $serviceDefinition) {
27+
if ($serviceDefinition->isAbstract()) {
28+
continue;
29+
}
30+
31+
/** @var class-string $objectName */
32+
$objectName = $objectType->getName();
33+
if (is_a($serviceDefinition->getType()->getName(), $objectName, true)) {
34+
yield $this->container->get($serviceDefinition->getType()->getName());
35+
}
36+
}
37+
}
38+
39+
abstract protected function doServiceGathering() : void;
40+
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace Cspray\AnnotatedContainer\Helper;
4+
5+
use Cspray\AnnotatedContainer\ServiceGatheringListener;
6+
use Cspray\Typiphy\ObjectType;
7+
8+
final class StubServiceGatheringListener extends ServiceGatheringListener {
9+
10+
private array $services = [];
11+
12+
public function __construct(
13+
private readonly ObjectType $serviceType
14+
) {}
15+
16+
protected function doServiceGathering() : void {
17+
foreach ($this->getServicesOfType($this->serviceType) as $service) {
18+
$this->services[] = $service;
19+
}
20+
}
21+
22+
public function getServices() : array {
23+
return $this->services;
24+
}
25+
}

test/ServiceGatheringListenerTest.php

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace Cspray\AnnotatedContainer;
4+
5+
use Cspray\AnnotatedContainer\Helper\StubServiceGatheringListener;
6+
use Cspray\AnnotatedContainerFixture\Fixtures;
7+
use PHPUnit\Framework\TestCase;
8+
9+
final class ServiceGatheringListenerTest extends TestCase {
10+
11+
public function testNoServicesReturnsEmptyArray() : void {
12+
$subject = new StubServiceGatheringListener(Fixtures::implicitAliasedServices()->fooInterface());
13+
eventEmitter()->registerListener($subject);
14+
$containerDefinition = compiler()->compile(
15+
ContainerDefinitionCompileOptionsBuilder::scanDirectories(Fixtures::singleConcreteService()->getPath())->build()
16+
);
17+
containerFactory()->createContainer($containerDefinition);
18+
19+
self::assertSame([] , $subject->getServices());
20+
}
21+
22+
public function testSingleConcreteServiceReturnsOneItemArray() : void {
23+
$subject = new StubServiceGatheringListener(Fixtures::implicitAliasedServices()->fooInterface());
24+
eventEmitter()->registerListener($subject);
25+
$containerDefinition = compiler()->compile(
26+
ContainerDefinitionCompileOptionsBuilder::scanDirectories(Fixtures::implicitAliasedServices()->getPath())->build()
27+
);
28+
$container = containerFactory()->createContainer($containerDefinition);
29+
30+
self::assertSame([$container->get(Fixtures::implicitAliasedServices()->fooImplementation()->getName())], $subject->getServices());
31+
}
32+
33+
public function testMultipleConcreteServices() : void {
34+
$subject = new StubServiceGatheringListener(Fixtures::ambiguousAliasedServices()->fooInterface());
35+
eventEmitter()->registerListener($subject);
36+
$containerDefinition = compiler()->compile(
37+
ContainerDefinitionCompileOptionsBuilder::scanDirectories(Fixtures::ambiguousAliasedServices()->getPath())->build()
38+
);
39+
$container = containerFactory()->createContainer($containerDefinition);
40+
41+
$services = $subject->getServices();
42+
usort($services, fn($a, $b) => $a::class <=> $b::class);
43+
44+
self::assertSame([
45+
$container->get(Fixtures::ambiguousAliasedServices()->barImplementation()->getName()),
46+
$container->get(Fixtures::ambiguousAliasedServices()->bazImplementation()->getName()),
47+
$container->get(Fixtures::ambiguousAliasedServices()->quxImplementation()->getName())
48+
], $services);
49+
}
50+
51+
}

0 commit comments

Comments
 (0)