From: Kunal Mehta Date: Thu, 14 May 2015 05:51:55 +0000 (-0700) Subject: registration: Allow extensions to specify which MW core versions they require X-Git-Tag: 1.31.0-rc.0~9925^2 X-Git-Url: http://git.cyclocoop.org/data/Luca_Pacioli_%28Gemaelde%29.jpeg?a=commitdiff_plain;h=cef1f31167177ca05c8981d61d1203172329345d;p=lhc%2Fweb%2Fwiklou.git registration: Allow extensions to specify which MW core versions they require This adds a "requires" property to extension.json, which extensions and skins can use to indicate which versions of MediaWiki core they support. The hacky wfUseMW() is now deprecated in favor of this. Rather than writing our own version constraint and parser library, we can re-use composer's, which was recently split out into a separate library named "composer/semver" for this patch. Any syntax accepted by composer[1] is available for usage here. Test cases have been provided to demonstrate how versions are parsed. For now it is recommended that people stick to expressing compatability with stable versions (e.g. ">= 1.26"). This patch does not support requiring specific MediaWiki core WMF branches, since those do not follow the standard semver format that composer parses. If we are unable to parse $wgVersion, all checking will be skipped and reported as compatible. [1] https://getcomposer.org/doc/01-basic-usage.md#package-versions Bug: T99084 Change-Id: I7785827216e16c596356d0ae42d6b30f3f179f10 --- diff --git a/autoload.php b/autoload.php index 82a45b49d5..b97e190bbf 100644 --- a/autoload.php +++ b/autoload.php @@ -273,6 +273,7 @@ $wgAutoloadLocalClasses = array( 'CopyJobQueue' => __DIR__ . '/maintenance/copyJobQueue.php', 'CoreParserFunctions' => __DIR__ . '/includes/parser/CoreParserFunctions.php', 'CoreTagHooks' => __DIR__ . '/includes/parser/CoreTagHooks.php', + 'CoreVersionChecker' => __DIR__ . '/includes/registration/CoreVersionChecker.php', 'CreateAndPromote' => __DIR__ . '/maintenance/createAndPromote.php', 'CreateFileOp' => __DIR__ . '/includes/filebackend/FileOp.php', 'CreditsAction' => __DIR__ . '/includes/actions/CreditsAction.php', diff --git a/composer.json b/composer.json index 852f2d2247..adfe01b8b0 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ "wiki": "https://www.mediawiki.org/" }, "require": { + "composer/semver": "0.1.0", "cssjanus/cssjanus": "1.1.1", "ext-iconv": "*", "leafo/lessphp": "0.5.0", diff --git a/docs/extension.schema.json b/docs/extension.schema.json index 1d78eccfc9..ece38fe9fb 100644 --- a/docs/extension.schema.json +++ b/docs/extension.schema.json @@ -270,6 +270,16 @@ "Unlicense" ] }, + "requires": { + "type": "object", + "description": "Indicates what versions of MediaWiki core are required. This syntax may be extended in the future, for example to check dependencies between other extensions.", + "properties": { + "MediaWiki": { + "type": "string", + "description": "Version constraint string against MediaWiki core." + } + } + }, "ResourceFileModulePaths": { "type": "object", "description": "Default paths to use for all ResourceLoader file modules", diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index 9d89633494..5e8b2c3aeb 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -3202,6 +3202,7 @@ function wfUsePHP( $req_ver ) { * * @see perldoc -f use * + * @deprecated since 1.26, use the "requires' property of extension.json * @param string|int|float $req_ver The version to check, can be a string, an integer, or a float * @throws MWException */ diff --git a/includes/registration/CoreVersionChecker.php b/includes/registration/CoreVersionChecker.php new file mode 100644 index 0000000000..a7a926c8fa --- /dev/null +++ b/includes/registration/CoreVersionChecker.php @@ -0,0 +1,68 @@ +versionParser = new VersionParser(); + try { + $this->coreVersion = new VersionConstraint( + '==', + $this->versionParser->normalize( $coreVersion ) + ); + } catch ( UnexpectedValueException $e ) { + // Non-parsable version, don't fatal. + } + } + + /** + * Check that the provided constraint is compatible with the current version of core + * + * @param string $constraint Something like ">= 1.26" + * @return bool + */ + public function check( $constraint ) { + if ( $this->coreVersion === false ) { + // Couldn't parse the core version, so we can't check anything + return true; + } + + return $this->versionParser->parseConstraints( $constraint ) + ->matches( $this->coreVersion ); + } +} diff --git a/includes/registration/ExtensionProcessor.php b/includes/registration/ExtensionProcessor.php index dc35347bdb..1ceded16ff 100644 --- a/includes/registration/ExtensionProcessor.php +++ b/includes/registration/ExtensionProcessor.php @@ -192,6 +192,16 @@ class ExtensionProcessor implements Processor { ); } + public function getRequirements( array $info ) { + $requirements = array(); + $key = ExtensionRegistry::MEDIAWIKI_CORE; + if ( isset( $info['requires'][$key] ) ) { + $requirements[$key] = $info['requires'][$key]; + } + + return $requirements; + } + protected function extractHooks( array $info ) { if ( isset( $info['Hooks'] ) ) { foreach ( $info['Hooks'] as $name => $value ) { diff --git a/includes/registration/ExtensionRegistry.php b/includes/registration/ExtensionRegistry.php index b89518ad28..63bee07343 100644 --- a/includes/registration/ExtensionRegistry.php +++ b/includes/registration/ExtensionRegistry.php @@ -11,6 +11,11 @@ */ class ExtensionRegistry { + /** + * "requires" key that applies to MediaWiki core/$wgVersion + */ + const MEDIAWIKI_CORE = 'MediaWiki'; + /** * Version of the highest supported manifest version */ @@ -156,8 +161,11 @@ class ExtensionRegistry { * @throws Exception */ public function readFromQueue( array $queue ) { + global $wgVersion; $autoloadClasses = array(); $processor = new ExtensionProcessor(); + $incompatible = array(); + $coreVersionParser = new CoreVersionChecker( $wgVersion ); foreach ( $queue as $path => $mtime ) { $json = file_get_contents( $path ); if ( $json === false ) { @@ -179,8 +187,27 @@ class ExtensionRegistry { // Set up the autoloader now so custom processors will work $GLOBALS['wgAutoloadClasses'] += $autoload; $autoloadClasses += $autoload; + // Check any constraints against MediaWiki core + $requires = $processor->getRequirements( $info ); + if ( isset( $requires[self::MEDIAWIKI_CORE] ) + && !$coreVersionParser->check( $requires[self::MEDIAWIKI_CORE] ) + ) { + // Doesn't match, mark it as incompatible. + $incompatible[] = "{$info['name']} is not compatible with the current " + . "MediaWiki core (version {$wgVersion}), it requires: ". $requires[self::MEDIAWIKI_CORE] + . '.'; + continue; + } + // Compatible, read and extract info $processor->extractInfo( $path, $info, $version ); } + if ( $incompatible ) { + if ( count( $incompatible ) === 1 ) { + throw new Exception( $incompatible[0] ); + } else { + throw new Exception( implode( "\n", $incompatible ) ); + } + } $data = $processor->getExtractedInfo(); // Need to set this so we can += to it later $data['globals']['wgAutoloadClasses'] = array(); diff --git a/includes/registration/Processor.php b/includes/registration/Processor.php index e1aaca73b7..e5669d2779 100644 --- a/includes/registration/Processor.php +++ b/includes/registration/Processor.php @@ -30,4 +30,14 @@ interface Processor { * 'attributes' - registration info which isn't a global variable */ public function getExtractedInfo(); + + /** + * Get the requirements for the provided info + * + * @since 1.26 + * @param array $info + * @return array Where keys are the name to have a constraint on, + * like 'MediaWiki'. Values are a constraint string like "1.26.1". + */ + public function getRequirements( array $info ); } diff --git a/tests/phpunit/includes/registration/CoreVersionCheckerTest.php b/tests/phpunit/includes/registration/CoreVersionCheckerTest.php new file mode 100644 index 0000000000..bc154b3713 --- /dev/null +++ b/tests/phpunit/includes/registration/CoreVersionCheckerTest.php @@ -0,0 +1,38 @@ +assertEquals( $expected, $checker->check( $constraint ) ); + } + + public static function provideCheck() { + return array( + // array( $wgVersion, constraint, expected ) + array( '1.25alpha', '>= 1.26', false ), + array( '1.25.0', '>= 1.26', false ), + array( '1.26alpha', '>= 1.26', true ), + array( '1.26alpha', '>= 1.26.0', true ), + array( '1.26alpha', '>= 1.26.0-stable', false ), + array( '1.26.0', '>= 1.26.0-stable', true ), + array( '1.26.1', '>= 1.26.0-stable', true ), + array( '1.27.1', '>= 1.26.0-stable', true ), + array( '1.26alpha', '>= 1.26.1', false ), + array( '1.26alpha', '>= 1.26alpha', true ), + array( '1.26alpha', '>= 1.25', true ), + array( '1.26.0-alpha.14', '>= 1.26.0-alpha.15', false ), + array( '1.26.0-alpha.14', '>= 1.26.0-alpha.10', true ), + array( '1.26.1', '>= 1.26.2, <=1.26.0', false ), + array( '1.26.1', '^1.26.2', false ), + // Accept anything for un-parsable version strings + array( '1.26mwf14', '== 1.25alpha', true ), + array( 'totallyinvalid', '== 1.0', true ), + ); + } +}