diff --git a/src/Di/Di.php b/src/Di/Di.php index 8e9a4a81..38ed1868 100644 --- a/src/Di/Di.php +++ b/src/Di/Di.php @@ -90,6 +90,34 @@ public static function isRegistered(string $abstract): bool return isset(self::$dependencies[$abstract]); } + /** + * Sets an instance into container + * @param string $abstract + * @param object $instance + * @return void + * @throws DiException + */ + public static function set(string $abstract, object $instance): void + { + if (!class_exists($abstract) && !interface_exists($abstract)) { + throw DiException::invalidAbstractDependency($abstract); + } + + if (!is_a($instance, $abstract)) { + throw DiException::invalidAbstractDependency($abstract); + } + + if (isset(self::$container[$abstract])) { + throw DiException::dependencyAlreadyRegistered($abstract); + } + + if (!isset(self::$dependencies[$abstract])) { + self::$dependencies[$abstract] = get_class($instance); + } + + self::$container[$abstract] = $instance; + } + /** * Retrieves a shared instance of the given dependency. * @param string $dependency diff --git a/tests/Unit/Di/DiTest.php b/tests/Unit/Di/DiTest.php index 8ca8a8a3..caff07a8 100644 --- a/tests/Unit/Di/DiTest.php +++ b/tests/Unit/Di/DiTest.php @@ -122,6 +122,65 @@ public function testDiAbstractToConcreteBinding() $this->assertInstanceOf(DummyService::class, $instance); } + public function testDiSetBindsInstanceToAbstract(): void + { + $instance = new DummyService(); + + Di::set(DummyServiceInterface::class, $instance); + + $resolved = Di::get(DummyServiceInterface::class); + + $this->assertSame($instance, $resolved); + $this->assertInstanceOf(DummyService::class, $resolved); + } + + public function testDiSetWorksWithoutPriorRegister(): void + { + $instance = new DummyService(); + + $this->assertFalse(Di::isRegistered(DummyServiceInterface::class)); + + Di::set(DummyServiceInterface::class, $instance); + + $this->assertTrue(Di::isRegistered(DummyServiceInterface::class)); + + $this->assertSame($instance, Di::get(DummyServiceInterface::class)); + } + + public function testDiSetRejectsWrongInstanceType(): void + { + $this->expectException(DiException::class); + $this->expectExceptionMessage( + 'The dependency `' . DummyServiceInterface::class . '` is not valid abstract class.' + ); + + Di::set(DummyServiceInterface::class, new \stdClass()); + } + + public function testDiSetRejectsInvalidAbstract(): void + { + $this->expectException(DiException::class); + $this->expectExceptionMessage( + 'The dependency `NonExistentInterface` is not valid abstract class.' + ); + + Di::set('NonExistentInterface', new DummyService()); + } + + public function testDiSetRejectsWhenAlreadyResolved(): void + { + Di::register(DummyService::class); + + Di::get(DummyService::class); // resolve singleton first + + $this->expectException(DiException::class); + $this->expectExceptionMessage( + 'The dependency `' . DummyService::class . '` is already registered.' + ); + + Di::set(DummyService::class, new DummyService()); + } + public function testDiGetCoreDependencies() { $this->assertInstanceOf(Loader::class, Di::get(Loader::class));