mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge pull request #4450 from sminnee/injector-method-calls
NEW: Add ‘calls’ section to Injector configs.
This commit is contained in:
commit
60f7c660d0
@ -585,15 +585,52 @@ class Injector {
|
||||
$objtype = $asType ? $asType : get_class($object);
|
||||
$mapping = isset($this->injectMap[$objtype]) ? $this->injectMap[$objtype] : null;
|
||||
|
||||
$spec = empty($this->specs[$objtype]) ? array() : $this->specs[$objtype];
|
||||
|
||||
// first off, set any properties defined in the service specification for this
|
||||
// object type
|
||||
if (isset($this->specs[$objtype]) && isset($this->specs[$objtype]['properties'])) {
|
||||
if(!empty($spec['properties']) && is_array($spec['properties'])) {
|
||||
foreach ($this->specs[$objtype]['properties'] as $key => $value) {
|
||||
$val = $this->convertServiceProperty($value);
|
||||
$this->setObjectProperty($object, $key, $val);
|
||||
}
|
||||
}
|
||||
|
||||
// Populate named methods
|
||||
if (!empty($spec['calls']) && is_array($spec['calls'])) {
|
||||
foreach ($spec['calls'] as $method) {
|
||||
// Ignore any blank entries from the array; these may be left in due to config system limitations
|
||||
if(!$method) continue;
|
||||
|
||||
// Format validation
|
||||
if(!is_array($method) || !isset($method[0]) || isset($method[2])) {
|
||||
throw new \InvalidArgumentException(
|
||||
"'calls' entries in service definition should be 1 or 2 element arrays."
|
||||
);
|
||||
}
|
||||
if(!is_string($method[0])) {
|
||||
throw new \InvalidArgumentException("1st element of a 'calls' entry should be a string");
|
||||
}
|
||||
if(isset($method[1]) && !is_array($method[1])) {
|
||||
throw new \InvalidArgumentException("2nd element of a 'calls' entry should an arguments array");
|
||||
}
|
||||
|
||||
// Check that the method exists and is callable
|
||||
$objectMethod = array($object, $method[0]);
|
||||
if (!is_callable($objectMethod)) {
|
||||
throw new \InvalidArgumentException("'$method[0]' in 'calls' entry is not a public method");
|
||||
}
|
||||
|
||||
// Call it
|
||||
call_user_func_array(
|
||||
$objectMethod,
|
||||
$this->convertServiceProperty(
|
||||
isset($method[1]) ? $method[1] : array()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// now, use any cached information about what properties this object type has
|
||||
// and set based on name resolution
|
||||
if (!$mapping) {
|
||||
|
@ -111,6 +111,16 @@ Now the dependencies will be replaced with our configuration.
|
||||
echo ($object->textProperty == 'My Text Value');
|
||||
// returns true;
|
||||
|
||||
As well as properties, method calls can also be specified:
|
||||
|
||||
:::yml
|
||||
Injector:
|
||||
Logger:
|
||||
class: Monolog\Logger
|
||||
calls:
|
||||
- [ pushHandler, [ %$DefaultHandler ] ]
|
||||
|
||||
|
||||
## Factories
|
||||
|
||||
Some services require non-trivial construction which means they must be created by a factory class. To do this, create
|
||||
|
@ -619,6 +619,91 @@ class InjectorTest extends SapphireTest {
|
||||
$this->assertInstanceOf('TestObject', $injector->get('service'));
|
||||
}
|
||||
|
||||
public function testMethods() {
|
||||
// do it again but have test object configured as a constructor dependency
|
||||
$injector = new Injector();
|
||||
$config = array(
|
||||
'A' => array(
|
||||
'class' => 'TestObject',
|
||||
),
|
||||
'B' => array(
|
||||
'class' => 'TestObject',
|
||||
),
|
||||
'TestService' => array(
|
||||
'class' => 'TestObject',
|
||||
'calls' => array(
|
||||
array('myMethod', array('%$A')),
|
||||
array('myMethod', array('%$B')),
|
||||
array('noArgMethod')
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$injector->load($config);
|
||||
$item = $injector->get('TestService');
|
||||
$this->assertTrue($item instanceof TestObject);
|
||||
$this->assertEquals(
|
||||
array($injector->get('A'), $injector->get('B'), 'noArgMethod called'),
|
||||
$item->methodCalls
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testNonExistentMethods() {
|
||||
$injector = new Injector();
|
||||
$config = array(
|
||||
'TestService' => array(
|
||||
'class' => 'TestObject',
|
||||
'calls' => array(
|
||||
array('thisDoesntExist')
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$injector->load($config);
|
||||
$item = $injector->get('TestService');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testProtectedMethods() {
|
||||
$injector = new Injector();
|
||||
$config = array(
|
||||
'TestService' => array(
|
||||
'class' => 'TestObject',
|
||||
'calls' => array(
|
||||
array('protectedMethod')
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$injector->load($config);
|
||||
$item = $injector->get('TestService');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testTooManyArrayValues() {
|
||||
$injector = new Injector();
|
||||
$config = array(
|
||||
'TestService' => array(
|
||||
'class' => 'TestObject',
|
||||
'calls' => array(
|
||||
array('method', array('args'), 'what is this?')
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$injector->load($config);
|
||||
$item = $injector->get('TestService');
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Test nesting of injector
|
||||
*/
|
||||
@ -713,10 +798,23 @@ class TestObject implements TestOnly {
|
||||
|
||||
public $sampleService;
|
||||
|
||||
public $methodCalls = array();
|
||||
|
||||
public function setSomething($v) {
|
||||
$this->sampleService = $v;
|
||||
}
|
||||
|
||||
public function myMethod($arg) {
|
||||
$this->methodCalls[] = $arg;
|
||||
}
|
||||
|
||||
public function noArgMethod() {
|
||||
$this->methodCalls[] = 'noArgMethod called';
|
||||
}
|
||||
|
||||
protected function protectedMethod() {
|
||||
$this->methodCalls[] = 'protectedMethod called';
|
||||
}
|
||||
}
|
||||
|
||||
class OtherTestObject implements TestOnly {
|
||||
|
Loading…
Reference in New Issue
Block a user