From: Kunal Mehta Date: Fri, 2 Feb 2018 10:27:27 +0000 (-0800) Subject: Use wikimedia/object-factory 1.0.0 X-Git-Tag: 1.31.0-rc.0~704^2 X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=commitdiff_plain;h=79de8fd02f20c8f8a9e22c537906151044f52979 Use wikimedia/object-factory 1.0.0 Deprecate the unnamespaced version and move it to includes/compat. Bug: T147167 Depends-On: I39c805bfb98b32f32f3d0dc1eee9e823afe1c21a Change-Id: I3780c7adf51683f3f7adb35a88f9a25a0a2e2530 --- diff --git a/RELEASE-NOTES-1.31 b/RELEASE-NOTES-1.31 index 00fad6afbd..b46d3d60ab 100644 --- a/RELEASE-NOTES-1.31 +++ b/RELEASE-NOTES-1.31 @@ -60,6 +60,7 @@ production. * … ==== New external libraries ==== +* Added wikimedia/object-factory 1.0.0 * … ==== Removed and replaced external libraries ==== @@ -211,6 +212,8 @@ changes to languages because of Phabricator reports. * OutputPage::readOnlyPage(); deprecated in 1.25 * OutputPage::rateLimited(); deprecated in 1.25 * The no-op method Skin::showIPinHeader(), deprecated in 1.27, was removed. +* \ObjectFactory (no namespace) is deprecated, the namespaced \Wikimedia\ObjectFactory + from the wikimedia/object-factory library should be used instead. == Compatibility == MediaWiki 1.31 requires PHP 5.5.9 or later. Although HHVM 3.18.5 or later is supported, diff --git a/autoload.php b/autoload.php index 168998e152..09abbfff08 100644 --- a/autoload.php +++ b/autoload.php @@ -1070,7 +1070,7 @@ $wgAutoloadLocalClasses = [ 'ORAField' => __DIR__ . '/includes/db/ORAField.php', 'ORAResult' => __DIR__ . '/includes/db/ORAResult.php', 'ObjectCache' => __DIR__ . '/includes/objectcache/ObjectCache.php', - 'ObjectFactory' => __DIR__ . '/includes/libs/ObjectFactory.php', + 'ObjectFactory' => __DIR__ . '/includes/compat/ObjectFactory.php', 'OldChangesList' => __DIR__ . '/includes/changes/OldChangesList.php', 'OldLocalFile' => __DIR__ . '/includes/filerepo/file/OldLocalFile.php', 'OracleInstaller' => __DIR__ . '/includes/installer/OracleInstaller.php', diff --git a/composer.json b/composer.json index 1730942f49..b2b02ae071 100644 --- a/composer.json +++ b/composer.json @@ -36,6 +36,7 @@ "wikimedia/composer-merge-plugin": "1.4.1", "wikimedia/html-formatter": "1.0.1", "wikimedia/ip-set": "1.2.0", + "wikimedia/object-factory": "1.0.0", "wikimedia/php-session-serializer": "1.0.4", "wikimedia/purtle": "1.0.6", "wikimedia/relpath": "2.1.1", diff --git a/includes/ServiceWiring.php b/includes/ServiceWiring.php index a89619f273..847c1bb23c 100644 --- a/includes/ServiceWiring.php +++ b/includes/ServiceWiring.php @@ -47,6 +47,7 @@ use MediaWiki\Shell\CommandFactory; use MediaWiki\Storage\BlobStoreFactory; use MediaWiki\Storage\RevisionStore; use MediaWiki\Storage\SqlBlobStore; +use Wikimedia\ObjectFactory; return [ 'DBLoadBalancerFactory' => function ( MediaWikiServices $services ) { diff --git a/includes/StubObject.php b/includes/StubObject.php index baf5109952..067f11f63e 100644 --- a/includes/StubObject.php +++ b/includes/StubObject.php @@ -19,6 +19,7 @@ * * @file */ +use Wikimedia\ObjectFactory; /** * Class to implement stub globals, which are globals that delay loading the diff --git a/includes/auth/AuthManager.php b/includes/auth/AuthManager.php index af070c25cb..47c0df5609 100644 --- a/includes/auth/AuthManager.php +++ b/includes/auth/AuthManager.php @@ -31,6 +31,7 @@ use Status; use StatusValue; use User; use WebRequest; +use Wikimedia\ObjectFactory; /** * This serves as the entry point to the authentication system. @@ -2292,7 +2293,7 @@ class AuthManager implements LoggerAwareInterface { $ret = []; foreach ( $specs as $spec ) { - $provider = \ObjectFactory::getObjectFromSpec( $spec ); + $provider = ObjectFactory::getObjectFromSpec( $spec ); if ( !$provider instanceof $class ) { throw new \RuntimeException( "Expected instance of $class, got " . get_class( $provider ) diff --git a/includes/compat/ObjectFactory.php b/includes/compat/ObjectFactory.php new file mode 100644 index 0000000000..7646238018 --- /dev/null +++ b/includes/compat/ObjectFactory.php @@ -0,0 +1,27 @@ + 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 - * contain 'class' or 'factory' keys - * @throws ReflectionException when 'args' are supplied and 'class' - * constructor is non-public or non-existent - */ - public static function getObjectFromSpec( $spec ) { - $args = isset( $spec['args'] ) ? $spec['args'] : []; - $expandArgs = !isset( $spec['closure_expansion'] ) || - $spec['closure_expansion'] === true; - - if ( $expandArgs ) { - $args = static::expandClosures( $args ); - } - - if ( isset( $spec['class'] ) ) { - $clazz = $spec['class']; - if ( !$args ) { - $obj = new $clazz(); - } else { - $obj = static::constructClassInstance( $clazz, $args ); - } - } elseif ( isset( $spec['factory'] ) ) { - $obj = call_user_func_array( $spec['factory'], $args ); - } else { - throw new InvalidArgumentException( - 'Provided specification lacks both factory and class parameters.' - ); - } - - 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( [ $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 ); - } - - /** - * Construct an instance of the given class using the given arguments. - * - * PHP's `call_user_func_array()` doesn't work with object construction so - * we have to use other measures. Starting with PHP 5.6.0 we could use the - * "splat" operator (`...`) to unpack the array into an argument list. - * Sadly there is no way to conditionally include a syntax construct like - * a new operator in a way that allows older versions of PHP to still - * parse the file. Instead, we will try a loop unrolling technique that - * works for 0-10 arguments. If we are passed 11 or more arguments we will - * take the performance penalty of using - * `ReflectionClass::newInstanceArgs()` to construct the desired object. - * - * @param string $clazz Class name - * @param array $args Constructor arguments - * @return mixed Constructed instance - */ - public static function constructClassInstance( $clazz, $args ) { - // $args should be a non-associative array; show nice error if that's not the case - if ( $args && array_keys( $args ) !== range( 0, count( $args ) - 1 ) ) { - throw new InvalidArgumentException( __METHOD__ . ': $args cannot be an associative array' ); - } - - // TODO: when PHP min version supported is >=5.6.0 replace this - // with `return new $clazz( ... $args );`. - $obj = null; - switch ( count( $args ) ) { - case 0: - $obj = new $clazz(); - break; - case 1: - $obj = new $clazz( $args[0] ); - break; - case 2: - $obj = new $clazz( $args[0], $args[1] ); - break; - case 3: - $obj = new $clazz( $args[0], $args[1], $args[2] ); - break; - case 4: - $obj = new $clazz( $args[0], $args[1], $args[2], $args[3] ); - break; - case 5: - $obj = new $clazz( - $args[0], $args[1], $args[2], $args[3], $args[4] - ); - break; - case 6: - $obj = new $clazz( - $args[0], $args[1], $args[2], $args[3], $args[4], - $args[5] - ); - break; - case 7: - $obj = new $clazz( - $args[0], $args[1], $args[2], $args[3], $args[4], - $args[5], $args[6] - ); - break; - case 8: - $obj = new $clazz( - $args[0], $args[1], $args[2], $args[3], $args[4], - $args[5], $args[6], $args[7] - ); - break; - case 9: - $obj = new $clazz( - $args[0], $args[1], $args[2], $args[3], $args[4], - $args[5], $args[6], $args[7], $args[8] - ); - break; - case 10: - $obj = new $clazz( - $args[0], $args[1], $args[2], $args[3], $args[4], - $args[5], $args[6], $args[7], $args[8], $args[9] - ); - break; - default: - // Fall back to using ReflectionClass and curse the developer - // who decided that 11+ args was a reasonable method - // signature. - $ref = new ReflectionClass( $clazz ); - $obj = $ref->newInstanceArgs( $args ); - } - return $obj; - } -} diff --git a/includes/libs/objectcache/MultiWriteBagOStuff.php b/includes/libs/objectcache/MultiWriteBagOStuff.php index 643f318315..b03052267a 100644 --- a/includes/libs/objectcache/MultiWriteBagOStuff.php +++ b/includes/libs/objectcache/MultiWriteBagOStuff.php @@ -20,6 +20,7 @@ * @file * @ingroup Cache */ +use Wikimedia\ObjectFactory; /** * A cache class that replicates all writes to multiple child caches. Reads diff --git a/includes/libs/objectcache/ReplicatedBagOStuff.php b/includes/libs/objectcache/ReplicatedBagOStuff.php index 8239491f72..56b6e0ecc2 100644 --- a/includes/libs/objectcache/ReplicatedBagOStuff.php +++ b/includes/libs/objectcache/ReplicatedBagOStuff.php @@ -18,6 +18,7 @@ * @file * @ingroup Cache */ +use Wikimedia\ObjectFactory; /** * A cache class that directs writes to one set of servers and reads to diff --git a/includes/session/SessionManager.php b/includes/session/SessionManager.php index 40a568ffa7..603985f50b 100644 --- a/includes/session/SessionManager.php +++ b/includes/session/SessionManager.php @@ -32,6 +32,7 @@ use Config; use FauxRequest; use User; use WebRequest; +use Wikimedia\ObjectFactory; /** * This serves as the entry point to the MediaWiki session handling system. @@ -429,7 +430,7 @@ final class SessionManager implements SessionManagerInterface { if ( $this->sessionProviders === null ) { $this->sessionProviders = []; foreach ( $this->config->get( 'SessionProviders' ) as $spec ) { - $provider = \ObjectFactory::getObjectFromSpec( $spec ); + $provider = ObjectFactory::getObjectFromSpec( $spec ); $provider->setLogger( $this->logger ); $provider->setConfig( $this->config ); $provider->setManager( $this ); diff --git a/includes/specialpage/SpecialPageFactory.php b/includes/specialpage/SpecialPageFactory.php index af688e225e..9469e69ce9 100644 --- a/includes/specialpage/SpecialPageFactory.php +++ b/includes/specialpage/SpecialPageFactory.php @@ -22,6 +22,7 @@ * @defgroup SpecialPage SpecialPage */ use MediaWiki\Linker\LinkRenderer; +use Wikimedia\ObjectFactory; /** * Factory for handling the special page list and generating SpecialPage objects. diff --git a/includes/user/CentralIdLookup.php b/includes/user/CentralIdLookup.php index 618b7f07ed..2a86f4a891 100644 --- a/includes/user/CentralIdLookup.php +++ b/includes/user/CentralIdLookup.php @@ -19,6 +19,7 @@ * * @file */ +use Wikimedia\ObjectFactory; /** * The CentralIdLookup service allows for connecting local users with diff --git a/tests/phpunit/includes/MessageTest.php b/tests/phpunit/includes/MessageTest.php index fa7782474a..70f4af9e78 100644 --- a/tests/phpunit/includes/MessageTest.php +++ b/tests/phpunit/includes/MessageTest.php @@ -1,5 +1,6 @@ ObjectFactoryTestFixture::class, - 'args' => [ - function () { - return 'wrapped'; - }, - 'unwrapped', - ], - 'calls' => [ - 'setter' => [ function () { - return 'wrapped'; - }, ], - ], - 'closure_expansion' => false, - ] ); - $this->assertInstanceOf( 'Closure', $obj->args[0] ); - $this->assertSame( 'wrapped', $obj->args[0]() ); - $this->assertSame( 'unwrapped', $obj->args[1] ); - $this->assertInstanceOf( 'Closure', $obj->setterArgs[0] ); - $this->assertSame( 'wrapped', $obj->setterArgs[0]() ); - } - - /** - * @covers ObjectFactory::getObjectFromSpec - * @covers ObjectFactory::expandClosures - */ - public function testClosureExpansionEnabled() { - $obj = ObjectFactory::getObjectFromSpec( [ - 'class' => ObjectFactoryTestFixture::class, - 'args' => [ - function () { - return 'wrapped'; - }, - 'unwrapped', - ], - 'calls' => [ - 'setter' => [ function () { - return 'wrapped'; - }, ], - ], - 'closure_expansion' => true, - ] ); - $this->assertInternalType( 'string', $obj->args[0] ); - $this->assertSame( 'wrapped', $obj->args[0] ); - $this->assertSame( 'unwrapped', $obj->args[1] ); - $this->assertInternalType( 'string', $obj->setterArgs[0] ); - $this->assertSame( 'wrapped', $obj->setterArgs[0] ); - - $obj = ObjectFactory::getObjectFromSpec( [ - 'class' => ObjectFactoryTestFixture::class, - 'args' => [ function () { - return 'unwrapped'; - }, ], - 'calls' => [ - 'setter' => [ 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] ); - } - - /** - * @covers ObjectFactory::getObjectFromSpec - */ - public function testGetObjectFromFactory() { - $args = [ 'a', 'b' ]; - $obj = ObjectFactory::getObjectFromSpec( [ - 'factory' => function ( $a, $b ) { - return new ObjectFactoryTestFixture( $a, $b ); - }, - 'args' => $args, - ] ); - $this->assertSame( $args, $obj->args ); - } - - /** - * @covers ObjectFactory::getObjectFromSpec - * @expectedException InvalidArgumentException - */ - public function testGetObjectFromInvalid() { - $args = [ 'a', 'b' ]; - $obj = ObjectFactory::getObjectFromSpec( [ - // Missing 'class' or 'factory' - 'args' => $args, - ] ); - } - - /** - * @covers ObjectFactory::getObjectFromSpec - * @dataProvider provideConstructClassInstance - */ - public function testGetObjectFromClass( $args ) { - $obj = ObjectFactory::getObjectFromSpec( [ - 'class' => ObjectFactoryTestFixture::class, - 'args' => $args, - ] ); - $this->assertSame( $args, $obj->args ); - } - - /** - * @covers ObjectFactory::constructClassInstance - * @dataProvider provideConstructClassInstance - */ - public function testConstructClassInstance( $args ) { - $obj = ObjectFactory::constructClassInstance( - 'ObjectFactoryTestFixture', $args - ); - $this->assertSame( $args, $obj->args ); - } - - public static function provideConstructClassInstance() { - // These args go to 11. I thought about making 10 one louder, but 11! - return [ - '0 args' => [ [] ], - '1 args' => [ [ 1, ] ], - '2 args' => [ [ 1, 2, ] ], - '3 args' => [ [ 1, 2, 3, ] ], - '4 args' => [ [ 1, 2, 3, 4, ] ], - '5 args' => [ [ 1, 2, 3, 4, 5, ] ], - '6 args' => [ [ 1, 2, 3, 4, 5, 6, ] ], - '7 args' => [ [ 1, 2, 3, 4, 5, 6, 7, ] ], - '8 args' => [ [ 1, 2, 3, 4, 5, 6, 7, 8, ] ], - '9 args' => [ [ 1, 2, 3, 4, 5, 6, 7, 8, 9, ] ], - '10 args' => [ [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ] ], - '11 args' => [ [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ] ], - ]; - } - - /** - * @covers ObjectFactory::constructClassInstance - * @expectedException InvalidArgumentException - */ - public function testNamedArgs() { - $args = [ 'foo' => 1, 'bar' => 2, 'baz' => 3 ]; - $obj = ObjectFactory::constructClassInstance( - 'ObjectFactoryTestFixture', $args - ); - } -} - -class ObjectFactoryTestFixture { - public $args; - public $setterArgs; - public function __construct( /*...*/ ) { - $this->args = func_get_args(); - } - public function setter( /*...*/ ) { - $this->setterArgs = func_get_args(); - } -} diff --git a/tests/phpunit/includes/preferences/DefaultPreferencesFactoryTest.php b/tests/phpunit/includes/preferences/DefaultPreferencesFactoryTest.php index 9c85df2fba..c10152344b 100644 --- a/tests/phpunit/includes/preferences/DefaultPreferencesFactoryTest.php +++ b/tests/phpunit/includes/preferences/DefaultPreferencesFactoryTest.php @@ -3,6 +3,7 @@ use MediaWiki\Auth\AuthManager; use MediaWiki\MediaWikiServices; use MediaWiki\Preferences\DefaultPreferencesFactory; +use Wikimedia\ObjectFactory; use Wikimedia\TestingAccessWrapper; /**