Merge "registration: Let extensions add PHP version requirements"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Mon, 24 Sep 2018 05:43:18 +0000 (05:43 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 24 Sep 2018 05:43:18 +0000 (05:43 +0000)
docs/extension.schema.v1.json
docs/extension.schema.v2.json
includes/registration/ExtensionDependencyError.php
includes/registration/ExtensionRegistry.php
includes/registration/VersionChecker.php
tests/phpunit/includes/registration/ExtensionProcessorTest.php
tests/phpunit/includes/registration/VersionCheckerTest.php

index 0ff169c..e6ec971 100644 (file)
                },
                "requires": {
                        "type": "object",
-                       "description": "Indicates what versions of MediaWiki core or extensions are required. This syntax may be extended in the future, for example to check dependencies between other services.",
+                       "description": "Indicates what versions of PHP, MediaWiki core or extensions are required. This syntax may be extended in the future, for example to check dependencies between other services.",
                        "additionalProperties": false,
                        "properties": {
                                "MediaWiki": {
                                        "type": "string",
                                        "description": "Version constraint string against MediaWiki core."
                                },
+                               "platform": {
+                                       "type": "object",
+                                       "description": "Indicates version constraints against platform services.",
+                                       "additionalProperties": false,
+                                       "properties": {
+                                               "php": {
+                                                       "type": "string",
+                                                       "description": "Version constraint string against PHP."
+                                               }
+                                       }
+                               },
                                "extensions": {
                                        "type": "object",
                                        "description": "Set of version constraint strings against specific extensions."
index 7de5ed5..93bf0d9 100644 (file)
                },
                "requires": {
                        "type": "object",
-                       "description": "Indicates what versions of MediaWiki core or extensions are required. This syntax may be extended in the future, for example to check dependencies between other services.",
+                       "description": "Indicates what versions of PHP, MediaWiki core or extensions are required. This syntax may be extended in the future, for example to check dependencies between other services.",
                        "additionalProperties": false,
                        "properties": {
                                "MediaWiki": {
                                        "type": "string",
                                        "description": "Version constraint string against MediaWiki core."
                                },
+                               "platform": {
+                                       "type": "object",
+                                       "description": "Indicates version constraints against platform services.",
+                                       "additionalProperties": false,
+                                       "properties": {
+                                               "php": {
+                                                       "type": "string",
+                                                       "description": "Version constraint string against PHP."
+                                               }
+                                       }
+                               },
                                "extensions": {
                                        "type": "object",
                                        "description": "Set of version constraint strings against specific extensions."
index d380d07..dfd5985 100644 (file)
@@ -48,6 +48,11 @@ class ExtensionDependencyError extends Exception {
         */
        public $incompatibleCore = false;
 
+       /**
+        * @var bool
+        */
+       public $incompatiblePhp = false;
+
        /**
         * @param array $errors Each error has a 'msg' and 'type' key at minimum
         */
@@ -59,6 +64,9 @@ class ExtensionDependencyError extends Exception {
                                case 'incompatible-core':
                                        $this->incompatibleCore = true;
                                        break;
+                               case 'incompatible-php':
+                                       $this->incompatiblePhp = true;
+                                       break;
                                case 'missing-skins':
                                        $this->missingSkins[] = $info['missing'];
                                        break;
index 1f8a27e..3138b37 100644 (file)
@@ -213,7 +213,8 @@ class ExtensionRegistry {
                $autoloadNamespaces = [];
                $autoloaderPaths = [];
                $processor = new ExtensionProcessor();
-               $versionChecker = new VersionChecker( $wgVersion );
+               $phpVersion = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION;
+               $versionChecker = new VersionChecker( $wgVersion, $phpVersion );
                $extDependencies = [];
                $incompatible = [];
                $warnings = false;
index 1569e08..93b4a14 100644 (file)
@@ -35,6 +35,11 @@ class VersionChecker {
         */
        private $coreVersion = false;
 
+       /**
+        * @var Constraint|bool representing PHP version
+        */
+       private $phpVersion = false;
+
        /**
         * @var array Loaded extensions
         */
@@ -48,9 +53,10 @@ class VersionChecker {
        /**
         * @param string $coreVersion Current version of core
         */
-       public function __construct( $coreVersion ) {
+       public function __construct( $coreVersion, $phpVersion ) {
                $this->versionParser = new VersionParser();
                $this->setCoreVersion( $coreVersion );
+               $this->setPhpVersion( $phpVersion );
        }
 
        /**
@@ -82,6 +88,21 @@ class VersionChecker {
                }
        }
 
+       /**
+        * Set PHP version.
+        *
+        * @param string $phpVersion Current PHP version. Must be well-formed.
+        * @throws UnexpectedValueException
+        */
+       private function setPhpVersion( $phpVersion ) {
+               // normalize to make this throw an exception if the version is invalid
+               $this->phpVersion = new Constraint(
+                       '==',
+                       $this->versionParser->normalize( $phpVersion )
+               );
+               $this->phpVersion->setPrettyString( $phpVersion );
+       }
+
        /**
         * Check all given dependencies if they are compatible with the named
         * installed extensions in the $credits array.
@@ -90,6 +111,9 @@ class VersionChecker {
         *     {
         *       'FooBar' => {
         *         'MediaWiki' => '>= 1.25.0',
+        *         'platform': {
+        *           'php': '>= 7.0.0'
+        *         },
         *         'extensions' => {
         *           'FooBaz' => '>= 1.25.0'
         *         },
@@ -108,14 +132,47 @@ class VersionChecker {
                        foreach ( $dependencies as $dependencyType => $values ) {
                                switch ( $dependencyType ) {
                                        case ExtensionRegistry::MEDIAWIKI_CORE:
-                                               $mwError = $this->handleMediaWikiDependency( $values, $extension );
+                                               $mwError = $this->handleDependency(
+                                                       $this->coreVersion,
+                                                       $values,
+                                                       $extension
+                                               );
                                                if ( $mwError !== false ) {
                                                        $errors[] = [
-                                                               'msg' => $mwError,
+                                                               'msg' =>
+                                                                       "{$extension} is not compatible with the current MediaWiki "
+                                                                       . "core (version {$this->coreVersion->getPrettyString()}), "
+                                                                       . "it requires: $values."
+                                                               ,
                                                                'type' => 'incompatible-core',
                                                        ];
                                                }
                                                break;
+                                       case 'platform':
+                                               foreach ( $values as $dependency => $constraint ) {
+                                                       if ( $dependency === 'php' ) {
+                                                               $phpError = $this->handleDependency(
+                                                                       $this->phpVersion,
+                                                                       $constraint,
+                                                                       $extension
+                                                               );
+                                                               if ( $phpError !== false ) {
+                                                                       $errors[] = [
+                                                                               'msg' =>
+                                                                                       "{$extension} is not compatible with the current PHP "
+                                                                                       . "version {$this->phpVersion->getPrettyString()}), "
+                                                                                       . "it requires: $constraint."
+                                                                               ,
+                                                                               'type' => 'incompatible-php',
+                                                                       ];
+                                                               }
+                                                       } else {
+                                                               // add other platform dependencies here
+                                                               throw new UnexpectedValueException( 'Dependency type ' . $dependency .
+                                                                       ' unknown in ' . $extension );
+                                                       }
+                                               }
+                                               break;
                                        case 'extensions':
                                        case 'skins':
                                                foreach ( $values as $dependency => $constraint ) {
@@ -138,29 +195,27 @@ class VersionChecker {
        }
 
        /**
-        * Handle a dependency to MediaWiki core. It will check, if a MediaWiki version constraint was
-        * set with self::setCoreVersion before this call (if not, it will return an empty array) and
-        * checks the version constraint given against it.
+        * Handle a simple dependency to MediaWiki core or PHP. See handleMediaWikiDependency and
+        * handlePhpDependency for details.
         *
+        * @param Constraint|bool $version The version installed
         * @param string $constraint The required version constraint for this dependency
         * @param string $checkedExt The Extension, which depends on this dependency
-        * @return bool|string false if no error, or a string with the message
+        * @return bool false if no error, true else
         */
-       private function handleMediaWikiDependency( $constraint, $checkedExt ) {
-               if ( $this->coreVersion === false ) {
-                       // Couldn't parse the core version, so we can't check anything
+       private function handleDependency( $version, $constraint, $checkedExt ) {
+               if ( $version === false ) {
+                       // Couldn't parse the version, so we can't check anything
                        return false;
                }
 
                // if the installed and required version are compatible, return an empty array
                if ( $this->versionParser->parseConstraints( $constraint )
-                       ->matches( $this->coreVersion ) ) {
+                       ->matches( $version ) ) {
                        return false;
                }
-               // otherwise mark this as incompatible.
-               return "{$checkedExt} is not compatible with the current "
-                       . "MediaWiki core (version {$this->coreVersion->getPrettyString()}), it requires: "
-                       . "$constraint.";
+
+               return true;
        }
 
        /**
index d9e091d..71a3a4f 100644 (file)
@@ -678,6 +678,9 @@ class ExtensionProcessorTest extends MediaWikiTestCase {
                $info = self::$default + [
                        'requires' => [
                                'MediaWiki' => '>= 1.25.0',
+                               'platform' => [
+                                       'php' => '>= 5.5.9'
+                               ],
                                'extensions' => [
                                        'Bar' => '*'
                                ]
index b668a9a..20f97bf 100644 (file)
@@ -9,10 +9,10 @@ class VersionCheckerTest extends PHPUnit\Framework\TestCase {
        use PHPUnit4And6Compat;
 
        /**
-        * @dataProvider provideCheck
+        * @dataProvider provideMediaWikiCheck
         */
-       public function testCheck( $coreVersion, $constraint, $expected ) {
-               $checker = new VersionChecker( $coreVersion );
+       public function testMediaWikiCheck( $coreVersion, $constraint, $expected ) {
+               $checker = new VersionChecker( $coreVersion, '7.0.0' );
                $this->assertEquals( $expected, !(bool)$checker->checkArray( [
                        'FakeExtension' => [
                                'MediaWiki' => $constraint,
@@ -20,7 +20,7 @@ class VersionCheckerTest extends PHPUnit\Framework\TestCase {
                ] ) );
        }
 
-       public static function provideCheck() {
+       public static function provideMediaWikiCheck() {
                return [
                        // [ $wgVersion, constraint, expected ]
                        [ '1.25alpha', '>= 1.26', false ],
@@ -44,11 +44,64 @@ class VersionCheckerTest extends PHPUnit\Framework\TestCase {
                ];
        }
 
+       /**
+        * @dataProvider providePhpValidCheck
+        */
+       public function testPhpValidCheck( $phpVersion, $constraint, $expected ) {
+               $checker = new VersionChecker( '1.0.0', $phpVersion );
+               $this->assertEquals( $expected, !(bool)$checker->checkArray( [
+                       'FakeExtension' => [
+                               'platform' => [
+                                       'php' => $constraint,
+                               ],
+                       ],
+               ] ) );
+       }
+
+       public static function providePhpValidCheck() {
+               return [
+                       // [ phpVersion, constraint, expected ]
+                       [ '7.0.23', '>= 7.0.0', true ],
+                       [ '7.0.23', '^7.1.0', false ],
+                       [ '7.0.23', '7.0.23', true ],
+               ];
+       }
+
+       /**
+        * @expectedException UnexpectedValueException
+        */
+       public function testPhpInvalidConstraint() {
+               $checker = new VersionChecker( '1.0.0', '7.0.0' );
+               $checker->checkArray( [
+                       'FakeExtension' => [
+                               'platform' => [
+                                       'php' => 'totallyinvalid',
+                               ],
+                       ],
+               ] );
+       }
+
+       /**
+        * @dataProvider providePhpInvalidVersion
+        * @expectedException UnexpectedValueException
+        */
+       public function testPhpInvalidVersion( $phpVersion ) {
+                $checker = new VersionChecker( '1.0.0', $phpVersion );
+       }
+
+       public static function providePhpInvalidVersion() {
+               return [
+                       // [ phpVersion ]
+                       [ '7.abc' ],
+                       [ '5.a.x' ],
+               ];
+       }
+
        /**
         * @dataProvider provideType
         */
        public function testType( $given, $expected ) {
-               $checker = new VersionChecker( '1.0.0' );
+               $checker = new VersionChecker( '1.0.0', '7.0.0' );
                $checker->setLoadedExtensionsAndSkins( [
                                'FakeDependency' => [
                                        'version' => '1.0.0',
@@ -150,7 +203,7 @@ class VersionCheckerTest extends PHPUnit\Framework\TestCase {
         * returns any error message.
         */
        public function testInvalidConstraint() {
-               $checker = new VersionChecker( '1.0.0' );
+               $checker = new VersionChecker( '1.0.0', '7.0.0' );
                $checker->setLoadedExtensionsAndSkins( [
                                'FakeDependency' => [
                                        'version' => 'not really valid',
@@ -169,7 +222,7 @@ class VersionCheckerTest extends PHPUnit\Framework\TestCase {
                        ],
                ] ) );
 
-               $checker = new VersionChecker( '1.0.0' );
+               $checker = new VersionChecker( '1.0.0', '7.0.0' );
                $checker->setLoadedExtensionsAndSkins( [
                                'FakeDependency' => [
                                        'version' => '1.24.3',
@@ -184,24 +237,49 @@ class VersionCheckerTest extends PHPUnit\Framework\TestCase {
                ] );
        }
 
-       /**
-        * T197478
-        */
-       public function testInvalidDependency() {
-               $checker = new VersionChecker( '1.0.0' );
-               $this->setExpectedException( UnexpectedValueException::class,
-                       'Dependency type skin unknown in FakeExtension' );
-               $this->assertEquals( [
+       public function provideInvalidDependency() {
+               return [
                        [
-                               'type' => 'invalid-version',
-                               'msg' => 'FakeDependency does not have a valid version string.',
+                               [
+                                       'FakeExtension' => [
+                                               'platform' => [
+                                                       'undefinedPlatformDependency' => '*',
+                                               ],
+                                       ],
+                               ],
+                               'undefinedPlatformDependency',
                        ],
-               ], $checker->checkArray( [
-                       'FakeExtension' => [
-                               'skin' => [
-                                       'FakeSkin' => '*',
+                       [
+                               [
+                                       'FakeExtension' => [
+                                               'undefinedDependencyType' => '*',
+                                       ],
                                ],
+                               'undefinedDependencyType',
                        ],
-               ] ) );
+                       // T197478
+                       [
+                               [
+                                       'FakeExtension' => [
+                                               'skin' => [
+                                                       'FakeSkin' => '*',
+                                               ],
+                                       ],
+                               ],
+                               'skin',
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideInvalidDependency
+        */
+       public function testInvalidDependency( $depencency, $type ) {
+               $checker = new VersionChecker( '1.0.0', '7.0.0' );
+               $this->setExpectedException(
+                       UnexpectedValueException::class,
+                       "Dependency type $type unknown in FakeExtension"
+               );
+               $checker->checkArray( $depencency );
        }
 }