Skip to content

Commit

Permalink
add support for composite types binding
Browse files Browse the repository at this point in the history
  • Loading branch information
Ondřej Ešler committed Feb 26, 2024
1 parent abb127f commit c5aec5a
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 12 deletions.
21 changes: 19 additions & 2 deletions src/ServiceContainer/ArgumentBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
namespace IW\ServiceContainer;

use IW\ServiceContainer;
use ReflectionIntersectionType;
use ReflectionNamedType;
use ReflectionParameter;
use ReflectionUnionType;

trait ArgumentBuilder
{
Expand All @@ -23,15 +25,19 @@ final protected function buildArgs(array $ids, ServiceContainer $container): arr
{
$args = [];

foreach ($ids as [$id, $isOptional, $default]) {
foreach ($ids as [$id, $isOptional, $default, $dnf]) {
if ($isOptional) {
$arg = $container->instance($id);

if ($arg === null) {
$arg = $default;
}
} else {
$arg = $container->get($id);
try {
$arg = $container->get($id);
} catch (ServiceNotFound $e) {
$dnf ? throw new CannotAutowireCompositType($id, $e) : throw $e;
}
}

$args[] = $arg;
Expand Down Expand Up @@ -67,6 +73,17 @@ final protected function resolveIds(): array
$classname,
$param->isOptional(),
$param->isOptional() ? $param->getDefaultValue() : null,
false,
];
continue;
}

if (($type instanceof ReflectionUnionType) || ($type instanceof ReflectionIntersectionType)) {
$ids[] = [
$type->__toString(),
$param->isOptional(),
$param->isOptional() ? $param->getDefaultValue() : null,
true,
];
continue;
}
Expand Down
14 changes: 14 additions & 0 deletions src/ServiceContainer/CannotAutowireCompositType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace IW\ServiceContainer;

class CannotAutowireCompositType extends Exception
{
/** @param class-string $id */
public function __construct(string $id, ServiceNotFound $previous)
{
parent::__construct('Cannot autowire composit type: ' . $id . ', define a factory for it', $previous);
}
}
12 changes: 12 additions & 0 deletions tests/Fix/ClassWithDnfType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace IW\Fix;

class ClassWithDnfType
{
public function __construct(private (Zero & Alias)|Fourth $dependency)
{
}
}
47 changes: 37 additions & 10 deletions tests/ServiceContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use IW\ServiceContainer\BrokenDependency;
use PHPUnit\Framework\TestCase;
use stdClass;
use Throwable;

use function random_bytes;
use function uniqid;
Expand Down Expand Up @@ -105,9 +106,9 @@ public function testBindingCustomFactory(): void

$this->assertIsObject($service);
$this->assertInstanceOf('stdClass', $service);
$this->assertObjectHasAttribute('foo', $service);
$this->assertObjectHasProperty('foo', $service);
$this->assertInstanceOf('IW\Fix\First', $service->foo);
$this->assertObjectHasAttribute('bar', $service);
$this->assertObjectHasProperty('bar', $service);
$this->assertSame($bar, $service->bar);
}

Expand Down Expand Up @@ -321,10 +322,13 @@ public function testUnionTypesCannotBeAutowired(): void
{
$container = new ServiceContainer();

$this->expectException('IW\ServiceContainer\UnsupportedAutowireParam');
$this->expectExceptionMessage('Unsupported type hint for param: Parameter #0 [ <required> IW\Fix\First|IW\Fix\Fourth $dependency ]');

$container->get('IW\Fix\ClassWithUnionType');
try {
$container->get('IW\Fix\ClassWithUnionType');
} catch (Throwable $e) {
$this->assertInstanceOf('IW\ServiceContainer\BrokenDependency', $e);
$this->assertInstanceOf('IW\ServiceContainer\CannotAutowireCompositType', $e->getPrevious());
$this->assertSame('Cannot autowire composit type: IW\Fix\First|IW\Fix\Fourth, define a factory for it', $e->getPrevious()->getMessage());
}
}

/** @requires PHP >= 8.1 */
Expand All @@ -342,10 +346,13 @@ public function testIntersectionTypesCannotBeAutowired(): void
{
$container = new ServiceContainer();

$this->expectException('IW\ServiceContainer\UnsupportedAutowireParam');
$this->expectExceptionMessage('Unsupported type hint for param: Parameter #0 [ <required> IW\Fix\Alias&IW\Fix\Zero $dependency ]');

$container->get('IW\Fix\ClassWithIntersectionType');
try {
$container->get('IW\Fix\ClassWithIntersectionType');
} catch (Throwable $e) {
$this->assertInstanceOf('IW\ServiceContainer\BrokenDependency', $e);
$this->assertInstanceOf('IW\ServiceContainer\CannotAutowireCompositType', $e->getPrevious());
$this->assertSame('Cannot autowire composit type: IW\Fix\Alias&IW\Fix\Zero, define a factory for it', $e->getPrevious()->getMessage());
}
}

public function testNullableParam(): void
Expand Down Expand Up @@ -381,4 +388,24 @@ public function testTry(): void
$this->expectException('ParseError');
$this->assertNull($container->try('IW\Fix\ClassWithSyntaxError'));
}

/** @requires PHP >= 8.2 */
public function testBindDnfType(): void
{
$container = new ServiceContainer();

$container->bind('(IW\Fix\Zero&IW\Fix\Alias)|IW\Fix\Fourth', static fn () => $container->get('IW\Fix\Zero'));

$this->assertInstanceOf('IW\Fix\ClassWithDnfType', $container->make('IW\Fix\ClassWithDnfType'));
}

/** @requires PHP >= 8.2 */
public function testAliasDnfType(): void
{
$container = new ServiceContainer();

$container->alias('(IW\Fix\Zero&IW\Fix\Alias)|IW\Fix\Fourth', 'IW\Fix\Fourth');

$this->assertInstanceOf('IW\Fix\ClassWithDnfType', $container->make('IW\Fix\ClassWithDnfType'));
}
}

0 comments on commit c5aec5a

Please sign in to comment.