From e53600c4b6471cd1391816570c423c8fd0828502 Mon Sep 17 00:00:00 2001 From: Bryan Davis Date: Thu, 30 Jul 2015 11:24:07 -0600 Subject: [PATCH] ObjectFactory: add support for setter injection Extend ObjectFactory::getObjectFromSpec() to support setter injection on created objects when the specification includes a 'calls' member. Bug: T107440 Change-Id: Ie2ece2e9658dd2d895d3935da4dc2da8a0a316e2 --- includes/libs/ObjectFactory.php | 49 ++++++++++++++----- .../includes/libs/ObjectFactoryTest.php | 25 ++++++++++ 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/includes/libs/ObjectFactory.php b/includes/libs/ObjectFactory.php index ec8c36a1df..1cb544b8b8 100644 --- a/includes/libs/ObjectFactory.php +++ b/includes/libs/ObjectFactory.php @@ -49,6 +49,13 @@ class ObjectFactory { * constructor/callable. This behavior can be suppressed by adding * closure_expansion => false to the specification. * + * The specification may also contain a 'calls' key that describes method + * calls to make on the newly created object before returning it. This + * pattern is often known as "setter injection". The value of this key is + * expected to be an associative array with method names as keys and + * argument lists as values. The argument list will be expanded (or not) + * in the same way as the 'args' key for the main object. + * * @param array $spec Object specification * @return object * @throws InvalidArgumentException when object specification does not @@ -58,18 +65,11 @@ class ObjectFactory { */ public static function getObjectFromSpec( $spec ) { $args = isset( $spec['args'] ) ? $spec['args'] : array(); + $expandArgs = !isset( $spec['closure_expansion'] ) || + $spec['closure_expansion'] === true; - if ( !isset( $spec['closure_expansion'] ) || - $spec['closure_expansion'] === true - ) { - $args = array_map( function ( $value ) { - if ( is_object( $value ) && $value instanceof Closure ) { - // If an argument is a Closure, call it. - return $value(); - } else { - return $value; - } - }, $args ); + if ( $expandArgs ) { + $args = static::expandClosures( $args ); } if ( isset( $spec['class'] ) ) { @@ -88,6 +88,33 @@ class ObjectFactory { ); } + if ( isset( $spec['calls'] ) && is_array( $spec['calls'] ) ) { + // Call additional methods on the newly created object + foreach ( $spec['calls'] as $method => $margs ) { + if ( $expandArgs ) { + $margs = static::expandClosures( $margs ); + } + call_user_func_array( array( $obj, $method ), $margs ); + } + } + return $obj; } + + /** + * Iterate a list and call any closures it contains. + * + * @param array $list List of things + * @return array List with any Closures replaced with their output + */ + protected static function expandClosures( $list ) { + return array_map( function ( $value ) { + if ( is_object( $value ) && $value instanceof Closure ) { + // If $value is a Closure, call it. + return $value(); + } else { + return $value; + } + }, $list ); + } } diff --git a/tests/phpunit/includes/libs/ObjectFactoryTest.php b/tests/phpunit/includes/libs/ObjectFactoryTest.php index a9d3cc1b20..aea037e0fe 100644 --- a/tests/phpunit/includes/libs/ObjectFactoryTest.php +++ b/tests/phpunit/includes/libs/ObjectFactoryTest.php @@ -29,10 +29,17 @@ class ObjectFactoryTest extends PHPUnit_Framework_TestCase { 'args' => array( function() { return 'unwrapped'; }, ), + 'calls' => array( + 'setter' => array( function() { + return 'unwrapped'; + }, ), + ), 'closure_expansion' => false, ) ); $this->assertInstanceOf( 'Closure', $obj->args[0] ); $this->assertSame( 'unwrapped', $obj->args[0]() ); + $this->assertInstanceOf( 'Closure', $obj->setterArgs[0] ); + $this->assertSame( 'unwrapped', $obj->setterArgs[0]() ); } /** @@ -44,25 +51,43 @@ class ObjectFactoryTest extends PHPUnit_Framework_TestCase { 'args' => array( function() { return 'unwrapped'; }, ), + 'calls' => array( + 'setter' => array( function() { + return 'unwrapped'; + }, ), + ), 'closure_expansion' => true, ) ); $this->assertInternalType( 'string', $obj->args[0] ); $this->assertSame( 'unwrapped', $obj->args[0] ); + $this->assertInternalType( 'string', $obj->setterArgs[0] ); + $this->assertSame( 'unwrapped', $obj->setterArgs[0] ); $obj = ObjectFactory::getObjectFromSpec( array( 'class' => 'ObjectFactoryTest_Fixture', 'args' => array( function() { return 'unwrapped'; }, ), + 'calls' => array( + 'setter' => array( function() { + return 'unwrapped'; + }, ), + ), ) ); $this->assertInternalType( 'string', $obj->args[0] ); $this->assertSame( 'unwrapped', $obj->args[0] ); + $this->assertInternalType( 'string', $obj->setterArgs[0] ); + $this->assertSame( 'unwrapped', $obj->setterArgs[0] ); } } class ObjectFactoryTest_Fixture { public $args; + public $setterArgs; public function __construct( /*...*/ ) { $this->args = func_get_args(); } + public function setter( /*...*/ ) { + $this->setterArgs = func_get_args(); + } } -- 2.20.1