diff --git a/src/ServiceContainer/ArgumentBuilder.php b/src/ServiceContainer/ArgumentBuilder.php index bff9052..682c0af 100644 --- a/src/ServiceContainer/ArgumentBuilder.php +++ b/src/ServiceContainer/ArgumentBuilder.php @@ -5,8 +5,10 @@ namespace IW\ServiceContainer; use IW\ServiceContainer; +use ReflectionIntersectionType; use ReflectionNamedType; use ReflectionParameter; +use ReflectionUnionType; trait ArgumentBuilder { @@ -23,7 +25,7 @@ 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); @@ -31,7 +33,11 @@ final protected function buildArgs(array $ids, ServiceContainer $container): arr $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; @@ -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; } diff --git a/src/ServiceContainer/CannotAutowireCompositType.php b/src/ServiceContainer/CannotAutowireCompositType.php new file mode 100644 index 0000000..28a7de3 --- /dev/null +++ b/src/ServiceContainer/CannotAutowireCompositType.php @@ -0,0 +1,14 @@ +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); } @@ -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 [ 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 */ @@ -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 [ 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 @@ -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')); + } }