mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
BUGFIX Make sure to only construct args for prototype object creation if
there are actually args passed through to prevent overwriting with null args if they're passed MINOR Added __get alias to remove need for explicit ->get() call MINOR Added the injector instance as an object that can be injected into other classes BUGFIX Fixed issue described in http://open.silverstripe.org/ticket/7448 whereby using the injector to create an object of a type already registered as a singleton would actually overwrite the stored singleton object
This commit is contained in:
parent
e23a7585a7
commit
56388ef1d8
@ -176,9 +176,15 @@ class Injector {
|
|||||||
*/
|
*/
|
||||||
public function __construct($config = null) {
|
public function __construct($config = null) {
|
||||||
$this->injectMap = array();
|
$this->injectMap = array();
|
||||||
$this->serviceCache = array();
|
$this->serviceCache = array(
|
||||||
|
'Injector' => $this,
|
||||||
|
);
|
||||||
|
$this->specs = array(
|
||||||
|
'Injector' => array('class' => 'Injector')
|
||||||
|
);
|
||||||
|
|
||||||
$this->autoProperties = array();
|
$this->autoProperties = array();
|
||||||
$this->specs = array();
|
|
||||||
|
|
||||||
$creatorClass = isset($config['creator']) ? $config['creator'] : 'InjectionCreator';
|
$creatorClass = isset($config['creator']) ? $config['creator'] : 'InjectionCreator';
|
||||||
$locatorClass = isset($config['locator']) ? $config['locator'] : 'ServiceConfigurationLocator';
|
$locatorClass = isset($config['locator']) ? $config['locator'] : 'ServiceConfigurationLocator';
|
||||||
@ -418,8 +424,14 @@ class Injector {
|
|||||||
*
|
*
|
||||||
* @param array $spec
|
* @param array $spec
|
||||||
* The specification of the class to instantiate
|
* 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)) {
|
if (is_string($spec)) {
|
||||||
$spec = array('class' => $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
|
// now set the service in place if needbe. This is NOT done for prototype beans, as they're
|
||||||
// created anew each time
|
// 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')) {
|
if ($id && (!$type || $type != 'prototype')) {
|
||||||
// this ABSOLUTELY must be set before the object is injected.
|
// this ABSOLUTELY must be set before the object is injected.
|
||||||
// This prevents circular reference errors down the line
|
// This prevents circular reference errors down the line
|
||||||
@ -656,7 +671,7 @@ class Injector {
|
|||||||
* Clear out all objects that are managed by the injetor.
|
* Clear out all objects that are managed by the injetor.
|
||||||
*/
|
*/
|
||||||
public function unregisterAllObjects() {
|
public function unregisterAllObjects() {
|
||||||
$this->serviceCache = array();
|
$this->serviceCache = array('Injector' => $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -691,12 +706,12 @@ class Injector {
|
|||||||
$spec = $this->specs[$serviceName];
|
$spec = $this->specs[$serviceName];
|
||||||
$type = isset($spec['type']) ? $spec['type'] : null;
|
$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 (($type && $type == 'prototype') || !$asSingleton) {
|
||||||
if ($spec) {
|
if ($spec && $constructorArgs) {
|
||||||
$spec['constructor'] = $constructorArgs;
|
$spec['constructor'] = $constructorArgs;
|
||||||
}
|
}
|
||||||
return $this->instantiate($spec, $serviceName);
|
return $this->instantiate($spec, $serviceName, !$type ? 'prototype' : $type);
|
||||||
} else {
|
} else {
|
||||||
if (!isset($this->serviceCache[$serviceName])) {
|
if (!isset($this->serviceCache[$serviceName])) {
|
||||||
$this->instantiate($spec, $serviceName);
|
$this->instantiate($spec, $serviceName);
|
||||||
@ -727,6 +742,17 @@ class Injector {
|
|||||||
return $this->instantiate($spec);
|
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
|
* Similar to get() but always returns a new object of the given type
|
||||||
*
|
*
|
||||||
|
@ -209,6 +209,42 @@ class InjectorTest extends SapphireTest {
|
|||||||
$sample = $injector->get('SampleService');
|
$sample = $injector->get('SampleService');
|
||||||
$this->assertEquals($sample->constructorVarOne, 'val1');
|
$this->assertEquals($sample->constructorVarOne, 'val1');
|
||||||
$this->assertEquals(get_class($sample->constructorVarTwo), 'AnotherService');
|
$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() {
|
public function testInjectUsingSetter() {
|
||||||
@ -421,6 +457,27 @@ class InjectorTest extends SapphireTest {
|
|||||||
$obj = $injector->get('MyChildClass');
|
$obj = $injector->get('MyChildClass');
|
||||||
$this->assertEquals($obj->one, 'the one');
|
$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 {
|
class TestObject {
|
||||||
|
Loading…
Reference in New Issue
Block a user