Skip to content

Commit

Permalink
Merge pull request #91 from gsteel/docs/delegators-and-aliases
Browse files Browse the repository at this point in the history
Better documentation for delegators and service aliases
  • Loading branch information
Ocramius authored Jul 24, 2021
2 parents 8032bc5 + de134d3 commit 2b0aee4
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 6 deletions.
12 changes: 6 additions & 6 deletions docs/book/cookbook/factories-vs-abstract-factories.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ As an example:

```php
return [
'factories' => [
SomeService::class => AnAbstractFactory::class,
],
'factories' => [
SomeService::class => AnAbstractFactory::class,
],
];
```

Expand Down Expand Up @@ -49,14 +49,14 @@ The service manager is optimized to locate *factories*, as it can do an
immediate hash table lookup; abstract factories involve:

- Looping through each abstract factory
- invoking its method for service location
- if the service is located, using the factory
- invoking its method for service location
- if the service is located, using the factory

This means, internally:

- a hash table lookup (for the abstract factory)
- invocation of 1:N methods for discovery
- which may contain additional lookups and/or retrievals in the container
- which may contain additional lookups and/or retrievals in the container
- invocation of a factory method (assuming successful lookup)

As such, having an explicit map can aid performance dramatically.
Expand Down
55 changes: 55 additions & 0 deletions docs/book/delegators.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,58 @@ around the instantiation logic of that particular service.

This latter point is the primary use case for delegators: *decorating the
instantiation logic for a service*.

## Delegator Factories and Service Aliases

In typical [service manager configurations](./configuring-the-service-manager.md) you have the opportunity to alias services. The following configuration would enable you to retrieve a `Buzzer` instance by its concrete implementation name and by the name of an interface that it implements, in this case, `BuzzerInterface`.

```php
$serviceManager = new Laminas\ServiceManager\ServiceManager([
'factories' => [
Buzzer::class => Laminas\ServiceManager\Factory\InvokableFactory::class,
],
'aliases' => [
BuzzerInterface::class => Buzzer::class,
],
]);
```

Currently, a delegator factory that targets an alias will not execute. Delegators must be configured using the resolved name of the service.

For example, given the following configuration, **no delegation would occur**:

```php
$serviceManager = new Laminas\ServiceManager\ServiceManager([
'factories' => [
Buzzer::class => Laminas\ServiceManager\Factory\InvokableFactory::class,
],
'aliases' => [
BuzzerInterface::class => Buzzer::class,
],
'delegators' => [
BuzzerInterface::class => [
BuzzerDelegatorFactory::class, // will not be executed
],
],
]);
```

In order for delegation to occur, the above configuration would need to be modified to target the resolved service name:

```php
$serviceManager = new Laminas\ServiceManager\ServiceManager([
'factories' => [
Buzzer::class => Laminas\ServiceManager\Factory\InvokableFactory::class,
],
'aliases' => [
BuzzerInterface::class => Buzzer::class,
],
'delegators' => [
Buzzer::class => [
BuzzerDelegatorFactory::class, // will now execute as expected
],
],
]);
```

Retrieving the `Buzzer` using its resolved name "`Buzzer::class`" or its alias "`BuzzerInterface::class`" will now both yield delegated instances.
53 changes: 53 additions & 0 deletions test/DelegatorAndAliasBehaviorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace LaminasTest\ServiceManager;

use Laminas\ServiceManager\Factory\InvokableFactory;
use Laminas\ServiceManager\ServiceManager;
use LaminasTest\ServiceManager\TestAsset\DelegatorAndAliasBehaviorTest\TargetObject;
use LaminasTest\ServiceManager\TestAsset\DelegatorAndAliasBehaviorTest\TargetObjectDelegator;
use PHPUnit\Framework\TestCase;

final class DelegatorAndAliasBehaviorTest extends TestCase
{
public function testThatADelegatorTargetingAServiceWillExecute(): void
{
$serviceManager = new ServiceManager([
'factories' => [
TargetObject::class => InvokableFactory::class,
],
'delegators' => [
TargetObject::class => [
TargetObjectDelegator::class,
],
],
]);

$service = $serviceManager->get(TargetObject::class);
self::assertInstanceOf(TargetObject::class, $service);
self::assertEquals(TargetObjectDelegator::DELEGATED_VALUE, $service->value);
}

public function testThatADelegatorWillNotExecuteWhenItTargetsAnAlias(): void
{
$serviceManager = new ServiceManager([
'factories' => [
TargetObject::class => InvokableFactory::class,
],
'aliases' => [
'Some Alias' => TargetObject::class,
],
'delegators' => [
'Some Alias' => [
TargetObjectDelegator::class,
],
],
]);

$service = $serviceManager->get('Some Alias');
self::assertInstanceOf(TargetObject::class, $service);
self::assertEquals(TargetObject::INITIAL_VALUE, $service->value);
}
}
18 changes: 18 additions & 0 deletions test/TestAsset/DelegatorAndAliasBehaviorTest/TargetObject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace LaminasTest\ServiceManager\TestAsset\DelegatorAndAliasBehaviorTest;

final class TargetObject
{
public const INITIAL_VALUE = 'Default';

/** @var string */
public $value;

public function __construct()
{
$this->value = self::INITIAL_VALUE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace LaminasTest\ServiceManager\TestAsset\DelegatorAndAliasBehaviorTest;

use Psr\Container\ContainerInterface;

use function assert;

final class TargetObjectDelegator
{
public const DELEGATED_VALUE = 'Delegated Value';

public function __invoke(ContainerInterface $container, string $serviceName, callable $callback): TargetObject
{
$service = $callback();
assert($service instanceof TargetObject);

$service->value = self::DELEGATED_VALUE;

return $service;
}
}

0 comments on commit 2b0aee4

Please sign in to comment.