Skip to content

Commit

Permalink
feature: introduce delegators for aliases
Browse files Browse the repository at this point in the history
This is just an initial commit and should provide the idea and a base to play around.
There are still some problems which can be reviewed in laminas#168

Signed-off-by: Maximilian Bösing <[email protected]>
  • Loading branch information
boesing committed May 3, 2023
1 parent 73e9d80 commit a3d3ebe
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 23 deletions.
50 changes: 29 additions & 21 deletions src/ServiceManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ public function get(string $id): mixed
// considerations out.
if (! $this->aliases) {
/** @psalm-suppress MixedAssignment Yes indeed, service managers can return mixed. */
$service = $this->doCreate($id);
$service = $this->doCreate(resolvedName: $id, aliasName: $id);

// Cache the service for later, if it is supposed to be shared.
if ($sharedService) {
Expand Down Expand Up @@ -248,7 +248,7 @@ public function get(string $id): mixed
// At this point, we have to create the object.
// We use the resolved name for that.
/** @psalm-suppress MixedAssignment Yes indeed, service managers can return mixed. */
$service = $this->doCreate($resolvedName);
$service = $this->doCreate(resolvedName: $resolvedName, aliasName: $id);

// Cache the object for later, if it is supposed to be shared.
if ($sharedService) {
Expand All @@ -263,9 +263,9 @@ public function get(string $id): mixed
public function build(string $name, ?array $options = null): mixed
{
// We never cache when using "build".
$name = $this->aliases[$name] ?? $name;
$resolvedName = $this->aliases[$name] ?? $name;
/** @psalm-suppress MixedReturnStatement Yes indeed, service managers can return mixed. */
return $this->doCreate($name, $options);
return $this->doCreate($resolvedName, $options, $name);
}

/**
Expand Down Expand Up @@ -590,13 +590,11 @@ private function getFactory(string $name): callable|FactoryInterface
));
}

private function createDelegatorFromName(string $name, ?array $options = null): mixed
private function createDelegatorFromName(string $name, callable $creationCallback, ?array $options = null): ?callable
{
$creationCallback = function () use ($name, $options) {
// Code is inlined for performance reason, instead of abstracting the creation
$factory = $this->getFactory($name);
return $factory($this->creationContext, $name, $options);
};
if (! isset($this->delegators[$name])) {
return $creationCallback;
}

$initialCreationContext = $this->creationContext;

Expand All @@ -611,7 +609,7 @@ private function createDelegatorFromName(string $name, ?array $options = null):

$this->delegators[$name] = $resolvedDelegators;

return $creationCallback();
return $creationCallback;
}

/**
Expand All @@ -623,18 +621,10 @@ private function createDelegatorFromName(string $name, ?array $options = null):
* @throws ServiceNotCreatedException If an exception is raised when creating a service.
* @throws ContainerExceptionInterface If any other error occurs.
*/
private function doCreate(string $resolvedName, ?array $options = null): mixed
private function doCreate(string $resolvedName, ?array $options = null, string $aliasName = ''): mixed
{
try {
if (! isset($this->delegators[$resolvedName])) {
// Let's create the service by fetching the factory
$factory = $this->getFactory($resolvedName);
/** @psalm-suppress MixedAssignment Yes indeed, service managers can return mixed. */
$service = $factory($this->creationContext, $resolvedName, $options);
} else {
/** @psalm-suppress MixedAssignment Yes indeed, service managers can return mixed. */
$service = $this->createDelegatorFromName($resolvedName, $options);
}
$service = $this->createService($resolvedName, $options, $aliasName);
} catch (ContainerExceptionInterface $exception) {
throw $exception;
} catch (Exception $exception) {
Expand Down Expand Up @@ -987,4 +977,22 @@ private function resolveDelegatorFactory(DelegatorFactoryInterface|string|callab

return $delegatorFactory;
}

private function createService(string $resolvedName, ?array $options, string $aliasName): mixed
{
$factory = $this->getFactory($resolvedName);
$creationContext = $this->creationContext;
$creationCallback = static fn () => $factory($creationContext, $resolvedName, $options);

$delegator = $this->createDelegatorFromName($resolvedName, $creationCallback, $options);
if ($aliasName !== $resolvedName) {
$delegator = $this->createDelegatorFromName(
$aliasName,
$delegator,
$options
);
}

return $delegator();
}
}
4 changes: 2 additions & 2 deletions test/DelegatorAndAliasBehaviorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function testThatADelegatorTargetingAServiceWillExecute(): void
self::assertEquals(TargetObjectDelegator::DELEGATED_VALUE, $service->value);
}

public function testThatADelegatorWillNotExecuteWhenItTargetsAnAlias(): void
public function testThatADelegatorWillExecuteWhenItTargetsAnAlias(): void
{
$serviceManager = new ServiceManager([
'factories' => [
Expand All @@ -50,6 +50,6 @@ public function testThatADelegatorWillNotExecuteWhenItTargetsAnAlias(): void
$service = $serviceManager->get('Some Alias');

self::assertInstanceOf(TargetObject::class, $service);
self::assertEquals(TargetObject::INITIAL_VALUE, $service->value);
self::assertEquals(TargetObjectDelegator::DELEGATED_VALUE, $service->value);
}
}
1 change: 1 addition & 0 deletions test/LazyServiceIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ public function testOnlyOneProxyAutoloaderItsRegisteredOnSubsequentCalls(): void
],
'factories' => [
InvokableObject::class => InvokableFactory::class,
stdClass::class => InvokableFactory::class,
],
'delegators' => [
InvokableObject::class => [LazyServiceFactory::class],
Expand Down

0 comments on commit a3d3ebe

Please sign in to comment.