diff --git a/README.md b/README.md index 2e72040..13bda08 100644 --- a/README.md +++ b/README.md @@ -227,6 +227,9 @@ final class UserProfileFactory implements \Devanych\Di\FactoryInterface $container->setMultiple([ UserProfile::class => UserProfileFactory::class, + // Or without autowiring + // UserProfile::class => fn => UserProfileFactory(), + // UserProfile::class => new UserProfileFactory(), 'user_name' => 'Alexander', 'user_age' => 40, ]); diff --git a/src/Container.php b/src/Container.php index bc6075f..cc33137 100644 --- a/src/Container.php +++ b/src/Container.php @@ -10,12 +10,10 @@ use Psr\Container\ContainerInterface; use ReflectionClass; use ReflectionException; -use Throwable; use function array_key_exists; use function class_exists; use function gettype; -use function in_array; use function is_string; use function sprintf; @@ -84,7 +82,7 @@ public function get($id) return $this->instances[$id]; } - $this->instances[$id] = $this->createInstance($id); + $this->instances[$id] = $this->getNew($id); return $this->instances[$id]; } @@ -95,10 +93,17 @@ public function get($id) * @return mixed * @throws NotFoundException If not found definition in the container. * @throws ContainerException If unable to create instance. + * @psalm-suppress MixedAssignment */ public function getNew(string $id) { - return $this->createInstance($id); + $instance = $this->createInstance($id); + + if ($instance instanceof FactoryInterface) { + return $instance->create($this); + } + + return $instance; } /** @@ -161,10 +166,13 @@ private function createInstance(string $id) /** * Create object by class name. * + * If the object has dependencies in the constructor, it tries to create them too. + * * @param string $className * @return object * @throws ContainerException If unable to create object. * @psalm-suppress ArgumentTypeCoercion + * @psalm-suppress MixedAssignment */ private function createObject(string $className): object { @@ -174,33 +182,6 @@ private function createObject(string $className): object throw new ContainerException(sprintf('Unable to create object `%s`.', $className), 0, $e); } - if (in_array(FactoryInterface::class, $reflection->getInterfaceNames(), true)) { - try { - /** @var FactoryInterface $factory */ - $factory = $this->getObjectFromReflection($reflection); - return $factory->create($this); - } catch (ContainerException $e) { - throw $e; - } catch (Throwable $e) { - throw new ContainerException(sprintf('Unable to create object `%s`.', $className), 0, $e); - } - } - - return $this->getObjectFromReflection($reflection); - } - - /** - * Create object from reflection. - * - * If the object has dependencies in the constructor, it tries to create them too. - * - * @param ReflectionClass $reflection - * @return object - * @throws ContainerException If unable to create object. - * @psalm-suppress MixedAssignment - */ - private function getObjectFromReflection(ReflectionClass $reflection): object - { if (($constructor = $reflection->getConstructor()) === null) { return $reflection->newInstance(); } diff --git a/src/FactoryInterface.php b/src/FactoryInterface.php index ad72f55..456b20c 100644 --- a/src/FactoryInterface.php +++ b/src/FactoryInterface.php @@ -16,32 +16,29 @@ interface FactoryInterface * Example of use: * * ```php + * use Devanych\Di\Container; + * use Devanych\Di\FactoryInterface; + * use Psr\Container\ContainerInterface; + * * // Example of an ApplicationFactory test class: - * final class ApplicationFactory implements \Devanych\Di\FactoryInterface + * final class ApplicationFactory implements FactoryInterface * { - * public ?string $environment; - * - * public function __construct(string $environment = null) - * { - * $this->environment = $environment; - * } - * * public function create(ContainerInterface $container): Application * { * return new Application( * $container->get(Router::class), * $container->get(EmitterInterface::class), - * $this->environment ?? $container->get('environment'), + * $container->get('environment'), * ); * } * } * * // Example of setting dependencies: - * $container = new \Devanych\Di\Container([ + * $container = new Container([ * 'environment' => 'development', * Application::class => ApplicationFactory::class, - * Router::class => RouterFactory::class, - * EmitterInterface::class => EmitterFactory::class, + * Router::class => fn() => new RouterFactory(), + * EmitterInterface::class => new EmitterFactory(), * ]); * * // Creating an Application instance: diff --git a/tests/ContainerTest.php b/tests/ContainerTest.php index 09be92d..a6b2c4c 100644 --- a/tests/ContainerTest.php +++ b/tests/ContainerTest.php @@ -135,10 +135,23 @@ public function testGetSameObject(): void $this->assertSame($instance1, $instance2); } - public function testGetSameObjectFromFactory(): void + public function factoryDataProvider(): array + { + return [ + 'factory-class-name' => [DummyFactory::class], + 'factory-callable' => [fn() => new DummyFactory()], + 'factory-object' => [new DummyFactory()], + ]; + } + + /** + * @dataProvider factoryDataProvider + * @param $factory + */ + public function testGetSameObjectFromFactory($factory): void { $container = new Container(); - $container->set(DummyData::class, DummyFactory::class); + $container->set(DummyData::class, $factory); $this->assertNotNull($instance1 = $container->get(DummyData::class)); $this->assertNotNull($instance2 = $container->get(DummyData::class));