NEW: Add ‘calls’ section to Injector configs.

As well as properties, you can now configure a series of method calls in
your service definitions.
This commit is contained in:
Sam Minnee 2015-07-29 23:06:25 +12:00
parent c170f90d5e
commit a1f7dcafa2
3 changed files with 147 additions and 2 deletions

View File

@ -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) {

View File

@ -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

View File

@ -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 {