Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make service delegate profile awaree #400

Merged
merged 1 commit into from
Nov 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/Definition/DefinitionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,15 @@ public function serviceType() : Type {
return $this->serviceType;
}

public function profiles() : array {
$profiles = $this->attribute->profiles();
if ($profiles === []) {
$profiles = ['default'];
}

return $profiles;
}

public function attribute() : ServiceDelegateAttribute {
return $this->attribute;
}
Expand Down
30 changes: 13 additions & 17 deletions src/Definition/ProfilesAwareContainerDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,10 @@ public function __construct(
}

public function serviceDefinitions() : array {
$filtered = [];
foreach ($this->containerDefinition->serviceDefinitions() as $serviceDefinition) {
if ($this->hasActiveProfile($serviceDefinition)) {
$filtered[] = $serviceDefinition;
}
}

return $filtered;
return array_values(array_filter(
$this->containerDefinition->serviceDefinitions(),
fn(ServiceDefinition $definition) => $this->hasActiveProfile($definition)
));
}

public function aliasDefinitions() : array {
Expand Down Expand Up @@ -50,17 +46,17 @@ public function servicePrepareDefinitions() : array {
}

public function serviceDelegateDefinitions() : array {
return $this->containerDefinition->serviceDelegateDefinitions();
return array_values(array_filter(
$this->containerDefinition->serviceDelegateDefinitions(),
fn(ServiceDelegateDefinition $definition) => $this->hasActiveProfile($definition)
));
}

public function injectDefinitions() : array {
$filtered = [];
foreach ($this->containerDefinition->injectDefinitions() as $injectDefinition) {
if ($this->hasActiveProfile($injectDefinition)) {
$filtered[] = $injectDefinition;
}
}
return $filtered;
return array_values(array_filter(
$this->containerDefinition->injectDefinitions(),
fn(InjectDefinition $definition) => $this->hasActiveProfile($definition)
));
}

private function getServiceDefinition(Type $objectType) : ?ServiceDefinition {
Expand All @@ -73,7 +69,7 @@ private function getServiceDefinition(Type $objectType) : ?ServiceDefinition {
return null;
}

private function hasActiveProfile(ServiceDefinition|InjectDefinition $definition) : bool {
private function hasActiveProfile(ServiceDefinition|InjectDefinition|ServiceDelegateDefinition $definition) : bool {
return $this->activeProfiles->isAnyActive($definition->profiles());
}
}
5 changes: 5 additions & 0 deletions src/Definition/ServiceDelegateDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,10 @@ public function delegateMethod() : string;

public function serviceType() : Type;

/**
* @return list<non-empty-string>
*/
public function profiles() : array;

public function attribute() : ServiceDelegateAttribute;
}
5 changes: 3 additions & 2 deletions src/Function/definitions.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,13 @@ function service(Type $type, ?string $name = null, array $profiles = [], bool $i
/**
* @param Type $factoryClass
* @param non-empty-string $factoryMethod
* @param list<non-empty-string> $profiles
*/
function serviceDelegate(Type $factoryClass, string $factoryMethod) : ServiceDelegateDefinition {
function serviceDelegate(Type $factoryClass, string $factoryMethod, array $profiles = []) : ServiceDelegateDefinition {
return definitionFactory()->serviceDelegateDefinitionFromClassMethodAndAttribute(
$factoryClass,
$factoryMethod,
new ServiceDelegateFromFunctionalApi()
new ServiceDelegateFromFunctionalApi($profiles)
);
}

Expand Down
15 changes: 15 additions & 0 deletions src/Internal/ServiceDelegateFromFunctionalApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,22 @@
*/
final class ServiceDelegateFromFunctionalApi implements ServiceDelegateAttribute {

public function __construct(
/**
* @var list<non-empty-string>
*/
private readonly array $profiles = [],
) {
}

public function service() : ?string {
return null;
}

/**
* @return list<non-empty-string>
*/
public function profiles() : array {
return $this->profiles;
}
}
4 changes: 4 additions & 0 deletions test/Fixture/Fixtures.php
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,8 @@ public static function thirdPartyKitchenSink() : ThirdPartyKitchenSinkFixture {
public static function injectServiceCollectionDecorator() : InjectServiceCollectionDecoratorFixture {
return new InjectServiceCollectionDecoratorFixture();
}

public static function profileAwareServiceDelegate() : ProfileAwareServiceDelegateFixture {
return new ProfileAwareServiceDelegateFixture();
}
}
18 changes: 18 additions & 0 deletions test/Fixture/ProfileAwareServiceDelegate/Factory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php declare(strict_types=1);

namespace Cspray\AnnotatedContainer\Fixture\ProfileAwareServiceDelegate;

use Cspray\AnnotatedContainer\Attribute\ServiceDelegate;

final class Factory {

#[ServiceDelegate(profiles: ['test'])]
public static function testService() : Service {
return new TestService();
}

#[ServiceDelegate(profiles: ['prod'])]
public static function prodService() : Service {
return new ProdService();
}
}
11 changes: 11 additions & 0 deletions test/Fixture/ProfileAwareServiceDelegate/ProdService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php declare(strict_types=1);

namespace Cspray\AnnotatedContainer\Fixture\ProfileAwareServiceDelegate;

#[\Cspray\AnnotatedContainer\Attribute\Service]
final class ProdService implements Service {

public function get() : string {
return self::class;
}
}
9 changes: 9 additions & 0 deletions test/Fixture/ProfileAwareServiceDelegate/Service.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php declare(strict_types=1);

namespace Cspray\AnnotatedContainer\Fixture\ProfileAwareServiceDelegate;

#[\Cspray\AnnotatedContainer\Attribute\Service]
interface Service {

public function get() : string;
}
11 changes: 11 additions & 0 deletions test/Fixture/ProfileAwareServiceDelegate/TestService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php declare(strict_types=1);

namespace Cspray\AnnotatedContainer\Fixture\ProfileAwareServiceDelegate;

#[\Cspray\AnnotatedContainer\Attribute\Service]
final class TestService implements Service {

public function get() : string {
return self::class;
}
}
33 changes: 33 additions & 0 deletions test/Fixture/ProfileAwareServiceDelegateFixture.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php declare(strict_types=1);

namespace Cspray\AnnotatedContainer\Fixture;

use Cspray\AnnotatedContainer\Fixture\ProfileAwareServiceDelegate\Factory;
use Cspray\AnnotatedContainer\Fixture\ProfileAwareServiceDelegate\ProdService;
use Cspray\AnnotatedContainer\Fixture\ProfileAwareServiceDelegate\Service;
use Cspray\AnnotatedContainer\Fixture\ProfileAwareServiceDelegate\TestService;
use Cspray\AnnotatedContainer\Reflection\Type;
use function Cspray\AnnotatedContainer\Reflection\types;

final class ProfileAwareServiceDelegateFixture implements Fixture {

public function getPath() : string {
return __DIR__ . '/ProfileAwareServiceDelegate';
}

public function factory() : Type {
return types()->class(Factory::class);
}

public function service() : Type {
return types()->class(Service::class);
}

public function testService() : Type {
return types()->class(TestService::class);
}

public function prodService() : Type {
return types()->class(ProdService::class);
}
}
16 changes: 16 additions & 0 deletions test/Unit/ContainerFactory/ContainerFactoryTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -527,4 +527,20 @@ public function testCreatingServiceWithInjectServiceDomainCollection() : void {
$collectionInjector->collection->services
);
}

public static function profileAwareServiceDelegateProvider() : array {
return [
[['default', 'test'], Fixtures::profileAwareServiceDelegate()->testService()->name()],
[['default', 'prod'], Fixtures::profileAwareServiceDelegate()->prodService()->name()],
];
}

#[DataProvider('profileAwareServiceDelegateProvider')]
public function testCreatingServiceWithProfileAwareServiceDelegate(array $profiles, string $expected) : void {
$container = $this->getContainer(Fixtures::profileAwareServiceDelegate()->getPath(), Profiles::fromList($profiles));

$service = $container->get(Fixtures::profileAwareServiceDelegate()->service()->name());

self::assertSame($expected, $service->get());
}
}
42 changes: 42 additions & 0 deletions test/Unit/Definition/DefinitionFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,8 @@ public function testServiceDelegateDefinitionFromAnnotatedTargetWithValidParamet
Closure $definitionCreator
) : void {
$attribute = $this->createMock(ServiceDelegateAttribute::class);
$attribute->method('profiles')->willReturn([]);
$attribute->method('service')->willReturn(null);

$definition = ($definitionCreator->bindTo($this, $this))($attribute);

Expand All @@ -648,11 +650,17 @@ public function testServiceDelegateDefinitionFromAnnotatedTargetWithValidParamet
'createService',
$definition->delegateMethod(),
);
self::assertSame(
['default'],
$definition->profiles(),
);
self::assertSame($attribute, $definition->attribute());
}

public function testServiceDelegateDefinitionFromStaticFactoryReturnsSelfCreatesObject() : void {
$attribute = $this->createMock(ServiceDelegateAttribute::class);
$attribute->method('profiles')->willReturn([]);
$attribute->method('service')->willReturn(null);

$definition = $this->subject->serviceDelegateDefinitionFromClassMethodAndAttribute(
Fixtures::thirdPartyKitchenSink()->nonAnnotatedService(),
Expand All @@ -672,6 +680,40 @@ public function testServiceDelegateDefinitionFromStaticFactoryReturnsSelfCreates
'create',
$definition->delegateMethod(),
);
self::assertSame(
['default'],
$definition->profiles(),
);
self::assertSame($attribute, $definition->attribute());
}

#[DataProvider('serviceDelegateDefinitionCreatorProvider')]
/**
* @param Closure(ServiceDelegateAttribute):ServiceDelegateDefinition $definitionCreator
*/
public function testServiceDelegateWithExplicitProfilesRespected(Closure $definitionCreator) : void {
$attribute = $this->createMock(ServiceDelegateAttribute::class);
$attribute->method('profiles')->willReturn(['drip', 'hippopotamus', 'chameleon']);
$attribute->method('service')->willReturn(null);

$definition = ($definitionCreator->bindTo($this, $this))($attribute);

self::assertSame(
Fixtures::delegatedService()->serviceInterface(),
$definition->serviceType()
);
self::assertSame(
Fixtures::delegatedService()->serviceFactory(),
$definition->delegateType()
);
self::assertSame(
'createService',
$definition->delegateMethod(),
);
self::assertSame(
['drip', 'hippopotamus', 'chameleon'],
$definition->profiles(),
);
self::assertSame($attribute, $definition->attribute());
}

Expand Down
32 changes: 25 additions & 7 deletions test/Unit/Definition/ProfilesAwareContainerDefinitionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,33 @@ public function testGetServicePrepareDefinitionsDelegatesToInjectedContainerDefi
self::assertSame([], $subject->servicePrepareDefinitions());
}

public function testGetServiceDelegateDefinitionsDelegatesToInjectedContainerDefinition() : void {
$containerDefinition = $this->getMockBuilder(ContainerDefinition::class)->getMock();
$containerDefinition->expects($this->once())
->method('serviceDelegateDefinitions')
->willReturn([]);
public function testGetServiceDelegateReturnsOnlyThoseWithActiveProfiles() : void {
$containerDefinition = $this->containerDefinition(
serviceDelegateDefinitions: [
$this->serviceDelegateDefinition(
Fixtures::injectConstructorServices()->injectStringService(),
Fixtures::injectConstructorServices()->injectStringService(),
'create',
['appletree']
),
$one = $this->serviceDelegateDefinition(
Fixtures::injectConstructorServices()->injectIntService(),
Fixtures::injectConstructorServices()->injectIntService(),
'create',
['not', 'like', 'us']
),
$two = $this->serviceDelegateDefinition(
Fixtures::injectConstructorServices()->injectArrayService(),
Fixtures::injectConstructorServices()->injectArrayService(),
'create',
['default', 'us']
)
]
);

$subject = new ProfilesAwareContainerDefinition($containerDefinition, Profiles::fromList(['default']));
$subject = new ProfilesAwareContainerDefinition($containerDefinition, Profiles::fromList(['us']));

self::assertSame([], $subject->serviceDelegateDefinitions());
self::assertSame([$one, $two], $subject->serviceDelegateDefinitions());
}

public function testGetInjectDefinitionsRespectProfiles() : void {
Expand Down
5 changes: 4 additions & 1 deletion test/Unit/Helper/HasMockDefinitions.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,16 @@ private function servicePrepareDefinition(
private function serviceDelegateDefinition(
Type $service,
Type $factory,
string $method
string $method,
array $profiles = [],
) : ServiceDelegateDefinition {
$mock = $this->createMock(ServiceDelegateDefinition::class);
$mock->method('delegateType')->willReturn($factory);
$mock->method('delegateMethod')->willReturn($method);
$mock->method('serviceType')->willReturn($service);
$mock->method('profiles')->willReturn($profiles);
$attribute = $this->createMock(ServiceDelegateAttribute::class);
$attribute->method('profiles')->willReturn([]);
$attribute->method('service')->willReturn(null);
$mock->method('attribute')->willReturn($attribute);

Expand Down
10 changes: 10 additions & 0 deletions test/Unit/ThirdPartyFunctionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ public function testServiceDelegateDefinition() {
$this->assertSame(Fixtures::delegatedService()->serviceInterface()->name(), $serviceDelegateDefinition->serviceType()->name());
$this->assertSame(Fixtures::delegatedService()->serviceFactory()->name(), $serviceDelegateDefinition->delegateType()->name());
$this->assertSame('createService', $serviceDelegateDefinition->delegateMethod());
$this->assertSame(['default'], $serviceDelegateDefinition->profiles());
}

public function testServiceDelegateDefinitionWithExplicitProfiles() : void {
$serviceDelegateDefinition = serviceDelegate(Fixtures::delegatedService()->serviceFactory(), 'createService', ['the', 'love', 'plug']);

$this->assertSame(Fixtures::delegatedService()->serviceInterface()->name(), $serviceDelegateDefinition->serviceType()->name());
$this->assertSame(Fixtures::delegatedService()->serviceFactory()->name(), $serviceDelegateDefinition->delegateType()->name());
$this->assertSame('createService', $serviceDelegateDefinition->delegateMethod());
$this->assertSame(['the', 'love', 'plug'], $serviceDelegateDefinition->profiles());
}

public function testServicePrepareDefinition() {
Expand Down
Loading