diff --git a/src/DI/Container.php b/src/DI/Container.php index b949492c3..53b4a6765 100644 --- a/src/DI/Container.php +++ b/src/DI/Container.php @@ -140,6 +140,23 @@ public function get($name, $useProxy = false) throw new NotFoundException("No entry or class found for '$name'"); } + /** + * Performs injection on an existing instance + * + * @param object $instance Object to perform injection upon + * @throws InvalidArgumentException + * @throws DependencyException + * @return object $instance + */ + public function inject($instance) + { + $className = get_class($instance); + $definition = $this->definitionManager->getDefinition($className); + + $instance = $this->factory->injectInstance($definition, $instance); + return $instance; + } + /** * Define an object or a value in the container * diff --git a/src/DI/Factory.php b/src/DI/Factory.php index 36ac90fb5..40ca0a848 100644 --- a/src/DI/Factory.php +++ b/src/DI/Factory.php @@ -75,6 +75,34 @@ public function createInstance(ClassDefinition $classDefinition) return $instance; } + /** + * {@inheritdoc} + * @throws DependencyException + * @throws \DI\Definition\Exception\DefinitionException + */ + public function injectInstance(ClassDefinition $classDefinition, $instance) + { + try { + // Property injections + foreach ($classDefinition->getPropertyInjections() as $propertyInjection) { + $this->injectProperty($instance, $propertyInjection); + } + + // Method injections + foreach ($classDefinition->getMethodInjections() as $methodInjection) { + $this->injectMethod($instance, $methodInjection); + } + } catch (DependencyException $e) { + throw $e; + } catch (DefinitionException $e) { + throw $e; + } catch (NotFoundException $e) { + throw new DependencyException("Error while injecting dependencies into " . $classDefinition->getClassName() . ": " . $e->getMessage(), 0, $e); + } + + return $instance; + } + /** * Creates an instance and inject dependencies through the constructor * diff --git a/src/DI/FactoryInterface.php b/src/DI/FactoryInterface.php index 8621a0454..9e7bfc144 100644 --- a/src/DI/FactoryInterface.php +++ b/src/DI/FactoryInterface.php @@ -27,5 +27,5 @@ interface FactoryInterface * @return object The instance */ function createInstance(ClassDefinition $classDefinition); - + function injectInstance(ClassDefinition $classDefinition, $instance); } diff --git a/tests/IntegrationTests/DI/InjectionTest.php b/tests/IntegrationTests/DI/InjectionTest.php index b410b5a59..6ecb02e74 100644 --- a/tests/IntegrationTests/DI/InjectionTest.php +++ b/tests/IntegrationTests/DI/InjectionTest.php @@ -14,6 +14,8 @@ use DI\Scope; use DI\Container; use IntegrationTests\DI\Fixtures\Class1; +use IntegrationTests\DI\Fixtures\Class2; +use IntegrationTests\DI\Fixtures\Implementation1; use IntegrationTests\DI\Fixtures\LazyDependency; /** @@ -167,6 +169,33 @@ public function testPropertyInjection($type, Container $container) $this->assertTrue($proxy->isProxyInitialized()); } + /** + * @dataProvider containerProvider + */ + public function testPropertyInjectionExistingObject($type, Container $container) + { + // Only constructor injection with reflection + if ($type == self::DEFINITION_REFLECTION) { + return; + } + /** @var $class1 Class1 */ + $class1 = new Class1(new Class2(), new Implementation1()); + $container->inject($class1); + $this->assertInstanceOf('IntegrationTests\DI\Fixtures\Class2', $class1->property1); + $this->assertInstanceOf('IntegrationTests\DI\Fixtures\Implementation1', $class1->property2); + $this->assertInstanceOf('IntegrationTests\DI\Fixtures\Class2', $class1->property3); + $this->assertEquals('bar', $class1->property4); + // Lazy injection + /** @var LazyDependency|\ProxyManager\Proxy\LazyLoadingInterface $proxy */ + $proxy = $class1->property5; + $this->assertInstanceOf('IntegrationTests\DI\Fixtures\LazyDependency', $proxy); + $this->assertInstanceOf('ProxyManager\Proxy\LazyLoadingInterface', $proxy); + $this->assertFalse($proxy->isProxyInitialized()); + // Correct proxy resolution + $this->assertTrue($proxy->getValue()); + $this->assertTrue($proxy->isProxyInitialized()); + } + /** * @dataProvider containerProvider */ @@ -193,6 +222,33 @@ public function testMethodInjection($type, Container $container) $this->assertTrue($proxy->isProxyInitialized()); } + /** + * @dataProvider containerProvider + */ + public function testMethodInjectionExistingObject($type, Container $container) + { + // Only constructor injection with reflection + if ($type == self::DEFINITION_REFLECTION) { + return; + } + /** @var $class1 Class1 */ + $class1 = new Class1(new Class2(), new Implementation1()); + $container->inject($class1); + $this->assertInstanceOf('IntegrationTests\DI\Fixtures\Class2', $class1->method1Param1); + $this->assertInstanceOf('IntegrationTests\DI\Fixtures\Implementation1', $class1->method2Param1); + $this->assertInstanceOf('IntegrationTests\DI\Fixtures\Class2', $class1->method3Param1); + $this->assertEquals('bar', $class1->method3Param2); + $this->assertInstanceOf('IntegrationTests\DI\Fixtures\LazyDependency', $class1->method4Param1); + $this->assertInstanceOf('ProxyManager\Proxy\LazyLoadingInterface', $class1->method4Param1); + // Lazy injection + /** @var LazyDependency|\ProxyManager\Proxy\LazyLoadingInterface $proxy */ + $proxy = $class1->method4Param1; + $this->assertFalse($proxy->isProxyInitialized()); + // Correct proxy resolution + $this->assertTrue($proxy->getValue()); + $this->assertTrue($proxy->isProxyInitialized()); + } + /** * @dataProvider containerProvider */ diff --git a/tests/UnitTests/DI/ContainerTest.php b/tests/UnitTests/DI/ContainerTest.php index 6227f9c90..1c3b22f87 100644 --- a/tests/UnitTests/DI/ContainerTest.php +++ b/tests/UnitTests/DI/ContainerTest.php @@ -152,6 +152,29 @@ public function testGetNonStringParameter() $container->get(new stdClass()); } + /** + * Test that injecting an existing object returns the same reference to that object + */ + public function testInjectMaintainsReferentialEquality() + { + $container = new Container(); + $instance = new stdClass(); + $result = $container->inject($instance); + + $this->assertSame($instance, $result); + } + + /** + * Test that injection on null yields null + */ + public function testInjectNull() + { + $container = new Container(); + $result = $container->inject(null); + + $this->assertEquals($result, null); + } + /** * We should be able to set a null value * @see https://github.com/mnapoli/PHP-DI/issues/79