Skip to content

Commit

Permalink
Implement ServiceExtensions with bacward compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
gmazzap committed Apr 30, 2024
1 parent 7e2852b commit a37ec01
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 3 deletions.
46 changes: 43 additions & 3 deletions src/Container/ReadOnlyContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,18 @@ class ReadOnlyContainer implements ContainerInterface
*
* @param array<string, callable(ContainerInterface $container):mixed> $services
* @param array<string, bool> $factoryIds
* @param ServiceExtensions $extensions
* @param ServiceExtensions|array $extensions
* @param ContainerInterface[] $containers
*/
public function __construct(
array $services,
array $factoryIds,
ServiceExtensions $extensions,
$extensions,
array $containers
) {
$this->services = $services;
$this->factoryIds = $factoryIds;
$this->extensions = $extensions;
$this->extensions = $this->configureServiceExtensions($extensions);
$this->containers = $containers;
}

Expand Down Expand Up @@ -116,4 +116,44 @@ public function has(string $id): bool

return false;
}

/**
* Support extensions as array or ServiceExtensions instance for backward compatibility.
*
* With PHP 8+ we could use an actual union type, but when we bump to PHP 8 as min supported
* version, we will probably bump major version as well, so we can just get rid of support
* for array.
*
* @param mixed $extensions
* @return ServiceExtensions
*/
private function configureServiceExtensions($extensions): ServiceExtensions
{
if ($extensions instanceof ServiceExtensions) {
return $extensions;
}

if (!is_array($extensions)) {
throw new \TypeError(
sprintf(
'%s::%s(): Argument #3 ($extensions) must be of type %s|array, %s given',
__CLASS__,
'__construct',
ServiceExtensions::class,
gettype($extensions)
)
);
}

$servicesExtensions = new ServiceExtensions();
foreach ($extensions as $id => $callback) {
/**
* @var string $id
* @var callable(mixed,ContainerInterface):mixed $callback
*/
$servicesExtensions->add($id, $callback);
}

return $servicesExtensions;
}
}
31 changes: 31 additions & 0 deletions tests/unit/Container/ReadOnlyContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,37 @@ public function count(): int
static::assertSame(1, $testee->get($expectedFactoryKey)->count());
}

/**
* @test
*/
public function testServiceExtensionsBackwardCompatibility(): void
{
$service = static fn (): object => (object) ['count' => 0];

Check failure on line 171 in tests/unit/Container/ReadOnlyContainerTest.php

View workflow job for this annotation

GitHub Actions / lint-php (7.2) / lint-php

Parse error: syntax error, unexpected 'fn' (T_STRING), expecting :: (T_PAAMAYIM_NEKUDOTAYIM) in ./tests/unit/Container/ReadOnlyContainerTest.php on line 171

Check failure on line 171 in tests/unit/Container/ReadOnlyContainerTest.php

View workflow job for this annotation

GitHub Actions / lint-php (7.2) / lint-php

Parse error: syntax error, unexpected 'fn' (T_STRING), expecting :: (T_PAAMAYIM_NEKUDOTAYIM) in ./tests/unit/Container/ReadOnlyContainerTest.php on line 171

Check failure on line 171 in tests/unit/Container/ReadOnlyContainerTest.php

View workflow job for this annotation

GitHub Actions / lint-php (7.3) / lint-php

Parse error: syntax error, unexpected 'fn' (T_STRING), expecting :: (T_PAAMAYIM_NEKUDOTAYIM) in ./tests/unit/Container/ReadOnlyContainerTest.php on line 171

Check failure on line 171 in tests/unit/Container/ReadOnlyContainerTest.php

View workflow job for this annotation

GitHub Actions / lint-php (7.3) / lint-php

Parse error: syntax error, unexpected 'fn' (T_STRING), expecting :: (T_PAAMAYIM_NEKUDOTAYIM) in ./tests/unit/Container/ReadOnlyContainerTest.php on line 171

$extension = static function (object $thing): object {
$thing->count++;

return $thing;
};

$container = new Container(['thing' => $service], [], ['thing' => $extension], []);

$resolved = $container->get('thing');

static::assertInstanceOf(\stdClass::class, $resolved);
static::assertSame(1, $resolved->count);
}

/**
* @test
*/
public function testServiceExtensionsBackwardCompatibilityBreaksOnWrongType(): void
{
$this->expectException(\TypeError::class);

new Container([], [], ServiceExtensions::class, []);
}

/**
* @param array $services
* @param array $factoryIds
Expand Down
98 changes: 98 additions & 0 deletions tests/unit/Container/ServiceExtensionsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

declare(strict_types=1);

namespace Inpsyde\Modularity\Tests\Unit\Container;

use Inpsyde\Modularity\Container\ServiceExtensions;
use Inpsyde\Modularity\Tests\TestCase;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;

class ServiceExtensionsTest extends TestCase
{
/**
* @test
*/
public function testBasicFunctionality(): void
{
$serviceExtensions = new ServiceExtensions();
$expected = 0;

$serviceExtensions->add(
'thing',
static function (object $thing) use (&$expected): object {
$thing->count++;
$expected++;

return $thing;
}
);

$serviceExtensions->add(
'nothing',
static function (object $thing): object {
$thing->count++;

return $thing;
}
);

$serviceExtensions->add(
'thing',
static function (object $thing) use (&$expected): object {
$thing->count++;
$expected++;

return $thing;
}
);

$serviceExtensions->add(
ServiceExtensions::typeId(\stdClass::class),
static function (object $thing) use (&$expected): object {
$thing->count++;
$expected++;

return $thing;
}
);

$serviceExtensions->add(
ServiceExtensions::typeId(\ArrayObject::class),
static function (object $thing): object {
$thing->count++;

return $thing;
}
);

$container = $this->stubContainer();
$thing = $serviceExtensions->resolve((object) ['count' => 0], 'thing', $container);

static::assertTrue($serviceExtensions->has('thing'));
static::assertTrue($serviceExtensions->has(ServiceExtensions::typeId(\stdClass::class)));
static::assertSame($expected, $thing->count);
}

/**
* @return ContainerInterface
*/
private function stubContainer(): ContainerInterface
{
return new class implements ContainerInterface
{
public function get(string $id)
{
throw new class () extends \Exception implements NotFoundExceptionInterface
{
};
}

public function has(string $id): bool
{
return false;
}
};
}
}

0 comments on commit a37ec01

Please sign in to comment.