Skip to content
Open
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [[*next-version*]] - YYYY-MM-DD
### Added
- Support for `psr/container:^2.0` (#22).

### Changed
- Drop PHP 7 and PHP 8.0 support, now requires PHP 8.1+ (#23).
- Service resolution now preserves keys, unless expected otherwise (#24).

### Deprecated
- `ServiceList` deprecated in favour of new `ServiceMap` (#24).

## [0.1.1-alpha3] - 2023-02-01
### Added
Expand Down
2 changes: 1 addition & 1 deletion src/Extension.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public function __construct(array $dependencies, callable $definition)
#[\Override]
public function __invoke(ContainerInterface $c, mixed $prev = null): mixed
{
$deps = $this->resolveDeps($c, $this->dependencies);
$deps = array_values($this->resolveDeps($c, $this->dependencies));
array_unshift($deps, $prev);

return ($this->definition)(...$deps);
Expand Down
2 changes: 1 addition & 1 deletion src/Factories/Constructor.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public function __construct(string $className, array $dependencies = [])
#[\Override]
public function __invoke(ContainerInterface $c): object
{
$deps = $this->resolveDeps($c, $this->dependencies);
$deps = array_values($this->resolveDeps($c, $this->dependencies));
$className = $this->className;

/** @psalm-suppress MixedMethodCall Cannot guarantee any particular class, just that it's a class */
Expand Down
19 changes: 2 additions & 17 deletions src/Factories/ServiceList.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,7 @@
* $list = $c->get('list'); // [5, "hello", 1.61803]
* ```
*
* The array of service keys may also be associative. The array keys will be preserved in the result.
*
* ```
* [
* 'foo' => Value(5),
* 'bar' => Value("hello"),
*
* 'config' => new ServiceList([
* 'num' => 'foo',
* 'msg' => 'bar'
* ]),
* ]
*
* $list = $c->get('list'); // ['num' => 5, 'msg' => "hello"]
* ```
*
* @deprecated Use {@see ServiceMap} with {@see array_values()} instead.
*/
class ServiceList extends Service
{
Expand All @@ -61,6 +46,6 @@ class ServiceList extends Service
#[\Override]
public function __invoke(ContainerInterface $c)
{
return $this->resolveDeps($c, $this->dependencies);
return array_values($this->resolveDeps($c, $this->dependencies));
}
}
49 changes: 49 additions & 0 deletions src/Factories/ServiceMap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

namespace Dhii\Services\Factories;

use Dhii\Services\ResolveKeysCapableTrait;
use Dhii\Services\Service;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;

/**
* A service that aggregates other services into a map.
*
* This implementation is configured with a map of scalars (keys) to service keys (values).
* Those keys will be resolved at call-time using the container,
* and the map of scalar keys to resolved values will be returned as the service value.
*
*
* ```
* [
* 'foo' => Value(5),
* 'bar' => Value("hello"),
*
* 'map' => new ServiceMap([
* 'num' => 'foo',
* 'msg' => 'bar'
* ]),
* ]
*
* $map = $c->get('map'); // ['num' => 5, 'msg' => "hello"]
* ```
*
*/
class ServiceMap extends Service
{
use ResolveKeysCapableTrait;

/**
* @inheritDoc
*
* @throws ContainerExceptionInterface If problem resolving from container.
*/
#[\Override]
public function __invoke(ContainerInterface $c)
{
return $this->resolveDeps($c, $this->dependencies);
}
}
2 changes: 1 addition & 1 deletion src/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public function __construct(array $dependencies, callable $definition)
#[\Override]
public function __invoke(ContainerInterface $c)
{
$deps = $this->resolveDeps($c, $this->dependencies);
$deps = array_values($this->resolveDeps($c, $this->dependencies));

return ($this->definition)(...$deps);
}
Expand Down
9 changes: 5 additions & 4 deletions src/ResolveKeysCapableTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ trait ResolveKeysCapableTrait
* @param array<string|callable> $keys The services keys to resolve.
* @psalm-param array<ServiceRef> $keys
*
* @return array<int,mixed> A list containing the resolved service values, in the same order as in $keys.
* @return array<array-key, mixed> A list containing the resolved service values, same order and keys as in $keys.
*
* @throws ContainerExceptionInterface If problem resolving from container.
*/
Expand All @@ -37,16 +37,17 @@ protected function resolveKeys(ContainerInterface $c, array $keys): array
* @param array<string|callable> $deps The list of dependencies, where each is either a callable definitions or key.
* @psalm-param ServiceRef[] $deps
*
* @return array<int,mixed> A list containing the resolved dependencies, in the same order as given in $keys.
* @return array<array-key, mixed> A list containing the resolved service values, same order and keys as in $keys.
* If a dep is scalar and the key isn't a srin
*
* @throws ContainerExceptionInterface If problem resolving from container.
*/
protected function resolveDeps(ContainerInterface $c, array $deps): array
{
$result = [];
foreach ($deps as $dep) {
foreach ($deps as $key => $dep) {
/** @psalm-suppress MixedAssignment We can't know the type that will be resolved */
$result[] = $this->resolveSingleDep($c, $dep);
$result[is_scalar($dep) && !is_string($key) ? strval($dep) : $key] = $this->resolveSingleDep($c, $dep);
}

return $result;
Expand Down
3 changes: 2 additions & 1 deletion tests/helpers/MockContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ public static function with(TestCase $tCase, array $services)
throw new ((string) (new ClassBuilder())
->withExtends(Exception::class)
->withImplements([NotFoundExceptionInterface::class])
)();

)("Key '{$key}' does not exist");
}

return $services[$key];
Expand Down
3 changes: 1 addition & 2 deletions tests/unit/Extensions/ArrayExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,13 @@ public function testInvoke()
];

$keys = array_keys($services);
$values = array_values($services);

$container = MockContainer::with($this, $services);

$subject = new ArrayExtension($keys);
$result = $subject($container, $prev);

static::assertEquals(array_merge($prev, $values), $result);
static::assertEquals(array_merge($prev, $services), $result);
}

/**
Expand Down
68 changes: 68 additions & 0 deletions tests/unit/Factories/ServiceMapTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace Dhii\Services\Tests\Unit\Factories;

use Dhii\Services\Factories\ServiceMap;
use Dhii\Services\Service;
use Dhii\Services\Tests\Helpers\MockContainer;
use PHPUnit\Framework\TestCase;

/**
* @since [*next-version*]
* @see ServiceList
*/
class ServiceMapTest extends TestCase
{
/**
* @since [*next-version*]
*/
public function testIsService()
{
static::assertInstanceOf(Service::class, new ServiceMap([]));
}

/**
* @since [*next-version*]
*/
public function testGetDependencies()
{
$deps = ['foo', 'bar'];
$subject = new ServiceMap($deps);

static::assertEquals($deps, $subject->getDependencies());
}

public function testInvoke()
{
$services = [
'foo' => 'hello',
'bar' => 'world',
];

$values = array_values($services);
$map = array_combine([
'alpha',
'beta',
], array_keys($services));

$container = MockContainer::with($this, $services);

$subject = new ServiceMap($map);
$result = $subject($container);

static::assertEquals(array_combine(array_keys($map), $values), $result);
}

/**
* @since [*next-version*]
*/
public function testInvokeNoDeps()
{
$container = MockContainer::create($this);

$subject = new ServiceMap([]);
$result = $subject($container);

static::assertEmpty($result);
}
}
4 changes: 2 additions & 2 deletions tests/unit/ResolveKeysCapableTraitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ public function testResolveDeps()
$method = AccessibleMethod::create($subject, 'resolveDeps');

// The test
$deps = ['foo', $depService, $depCallable];
$deps = ['a' => 'foo', 'b' => $depService, 'c' => $depCallable];
$result = $method($container, $deps);
$expected = [$values[0], $depValue1, $depValue2];
$expected = ['a' => $values[0], 'b' => $depValue1, 'c' => $depValue2];

static::assertEquals($expected, $result);
}
Expand Down
Loading