diff --git a/control/injector/Injector.php b/control/injector/Injector.php index 233c64987..3e747995e 100644 --- a/control/injector/Injector.php +++ b/control/injector/Injector.php @@ -176,9 +176,15 @@ class Injector { */ public function __construct($config = null) { $this->injectMap = array(); - $this->serviceCache = array(); + $this->serviceCache = array( + 'Injector' => $this, + ); + $this->specs = array( + 'Injector' => array('class' => 'Injector') + ); + $this->autoProperties = array(); - $this->specs = array(); + $creatorClass = isset($config['creator']) ? $config['creator'] : 'InjectionCreator'; $locatorClass = isset($config['locator']) ? $config['locator'] : 'ServiceConfigurationLocator'; @@ -418,8 +424,14 @@ class Injector { * * @param array $spec * The specification of the class to instantiate + * @param string $id + * The name of the object being created. If not supplied, then the id will be inferred from the + * object being created + * @param string $type + * Whether to create as a singleton or prototype object. Allows code to be explicit as to how it + * wants the object to be returned */ - protected function instantiate($spec, $id=null) { + protected function instantiate($spec, $id=null, $type = null) { if (is_string($spec)) { $spec = array('class' => $spec); } @@ -441,7 +453,10 @@ class Injector { // now set the service in place if needbe. This is NOT done for prototype beans, as they're // created anew each time - $type = isset($spec['type']) ? $spec['type'] : null; + if (!$type) { + $type = isset($spec['type']) ? $spec['type'] : null; + } + if ($id && (!$type || $type != 'prototype')) { // this ABSOLUTELY must be set before the object is injected. // This prevents circular reference errors down the line @@ -656,7 +671,7 @@ class Injector { * Clear out all objects that are managed by the injetor. */ public function unregisterAllObjects() { - $this->serviceCache = array(); + $this->serviceCache = array('Injector' => $this); } /** @@ -691,12 +706,12 @@ class Injector { $spec = $this->specs[$serviceName]; $type = isset($spec['type']) ? $spec['type'] : null; - // if we're a prototype OR we're not wanting a singleton + // if we're explicitly a prototype OR we're not wanting a singleton if (($type && $type == 'prototype') || !$asSingleton) { - if ($spec) { + if ($spec && $constructorArgs) { $spec['constructor'] = $constructorArgs; } - return $this->instantiate($spec, $serviceName); + return $this->instantiate($spec, $serviceName, !$type ? 'prototype' : $type); } else { if (!isset($this->serviceCache[$serviceName])) { $this->instantiate($spec, $serviceName); @@ -726,6 +741,17 @@ class Injector { return $this->instantiate($spec); } + + /** + * Magic method to return an item directly + * + * @param string $name + * The named object to retrieve + * @return mixed + */ + public function __get($name) { + return $this->get($name); + } /** * Similar to get() but always returns a new object of the given type diff --git a/tests/injector/InjectorTest.php b/tests/injector/InjectorTest.php index 8ecbccaeb..a4cd9dfeb 100644 --- a/tests/injector/InjectorTest.php +++ b/tests/injector/InjectorTest.php @@ -209,6 +209,42 @@ class InjectorTest extends SapphireTest { $sample = $injector->get('SampleService'); $this->assertEquals($sample->constructorVarOne, 'val1'); $this->assertEquals(get_class($sample->constructorVarTwo), 'AnotherService'); + + $injector = new Injector(); + $config = array(array( + 'src' => TEST_SERVICES . '/SampleService.php', + 'constructor' => array( + 'val1', + 'val2', + ) + )); + + $injector->load($config); + $sample = $injector->get('SampleService'); + $this->assertEquals($sample->constructorVarOne, 'val1'); + $this->assertEquals($sample->constructorVarTwo, 'val2'); + + // test constructors on prototype + $injector = new Injector(); + $config = array(array( + 'type' => 'prototype', + 'src' => TEST_SERVICES . '/SampleService.php', + 'constructor' => array( + 'val1', + 'val2', + ) + )); + + $injector->load($config); + $sample = $injector->get('SampleService'); + $this->assertEquals($sample->constructorVarOne, 'val1'); + $this->assertEquals($sample->constructorVarTwo, 'val2'); + + $again = $injector->get('SampleService'); + $this->assertFalse($sample === $again); + + $this->assertEquals($sample->constructorVarOne, 'val1'); + $this->assertEquals($sample->constructorVarTwo, 'val2'); } public function testInjectUsingSetter() { @@ -421,6 +457,27 @@ class InjectorTest extends SapphireTest { $obj = $injector->get('MyChildClass'); $this->assertEquals($obj->one, 'the one'); } + + public function testSameNamedSingeltonPrototype() { + $injector = new Injector(); + + // get a singleton object + $object = $injector->get('NeedsBothCirculars'); + $object->var = 'One'; + + $again = $injector->get('NeedsBothCirculars'); + $this->assertEquals($again->var, 'One'); + + // create a NEW instance object + $new = $injector->create('NeedsBothCirculars'); + $this->assertNull($new->var); + + // this will trigger a problem below + $new->var = 'Two'; + + $again = $injector->get('NeedsBothCirculars'); + $this->assertEquals($again->var, 'One'); + } } class TestObject {