Merge pull request #4450 from sminnee/injector-method-calls

NEW: Add ‘calls’ section to Injector configs.
This commit is contained in:
Damian Mooyman 2015-07-30 21:35:06 +12:00
commit 60f7c660d0
3 changed files with 147 additions and 2 deletions

View File

@ -585,15 +585,52 @@ class Injector {
$objtype = $asType ? $asType : get_class($object); $objtype = $asType ? $asType : get_class($object);
$mapping = isset($this->injectMap[$objtype]) ? $this->injectMap[$objtype] : null; $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 // first off, set any properties defined in the service specification for this
// object type // 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) { foreach ($this->specs[$objtype]['properties'] as $key => $value) {
$val = $this->convertServiceProperty($value); $val = $this->convertServiceProperty($value);
$this->setObjectProperty($object, $key, $val); $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 // now, use any cached information about what properties this object type has
// and set based on name resolution // and set based on name resolution
if (!$mapping) { if (!$mapping) {

View File

@ -111,6 +111,16 @@ Now the dependencies will be replaced with our configuration.
echo ($object->textProperty == 'My Text Value'); echo ($object->textProperty == 'My Text Value');
// returns true; // returns true;
As well as properties, method calls can also be specified:
:::yml
Injector:
Logger:
class: Monolog\Logger
calls:
- [ pushHandler, [ %$DefaultHandler ] ]
## Factories ## Factories
Some services require non-trivial construction which means they must be created by a factory class. To do this, create Some services require non-trivial construction which means they must be created by a factory class. To do this, create

View File

@ -619,6 +619,91 @@ class InjectorTest extends SapphireTest {
$this->assertInstanceOf('TestObject', $injector->get('service')); $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 * Test nesting of injector
*/ */
@ -713,10 +798,23 @@ class TestObject implements TestOnly {
public $sampleService; public $sampleService;
public $methodCalls = array();
public function setSomething($v) { public function setSomething($v) {
$this->sampleService = $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 { class OtherTestObject implements TestOnly {
@ -848,4 +946,4 @@ class SSObjectCreator extends InjectionCreator {
return parent::create($class, $params); return parent::create($class, $params);
} }
} }
} }