From 934227923d5a9bb425f0692d2adef9cc5e0c6340 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 12 Nov 2024 16:42:52 +0100 Subject: [PATCH 1/4] Refactor ComposerPhpVersionFactory --- src/Php/ComposerPhpVersionFactory.php | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/Php/ComposerPhpVersionFactory.php b/src/Php/ComposerPhpVersionFactory.php index ebcf7c130e..81ac309dc6 100644 --- a/src/Php/ComposerPhpVersionFactory.php +++ b/src/Php/ComposerPhpVersionFactory.php @@ -3,16 +3,12 @@ namespace PHPStan\Php; use Composer\Semver\VersionParser; -use Nette\Utils\Json; -use Nette\Utils\JsonException; use Nette\Utils\Strings; -use PHPStan\File\CouldNotReadFileException; -use PHPStan\File\FileReader; +use PHPStan\Internal\ComposerHelper; use PHPStan\ShouldNotHappenException; use function count; use function end; use function is_array; -use function is_file; use function is_int; use function is_string; use function min; @@ -114,21 +110,18 @@ public function getMaxVersion(): ?PhpVersion private function getComposerRequireVersion(): ?string { $composerPhpVersion = null; + if (count($this->composerAutoloaderProjectPaths) > 0) { - $composerJsonPath = end($this->composerAutoloaderProjectPaths) . '/composer.json'; - if (is_file($composerJsonPath)) { - try { - $composerJsonContents = FileReader::read($composerJsonPath); - $composer = Json::decode($composerJsonContents, Json::FORCE_ARRAY); - $requiredVersion = $composer['require']['php'] ?? null; - if (is_string($requiredVersion)) { - $composerPhpVersion = $requiredVersion; - } - } catch (CouldNotReadFileException | JsonException) { - // pass + $composer = ComposerHelper::getComposerConfig(end($this->composerAutoloaderProjectPaths)); + if ($composer !== null) { + $requiredVersion = $composer['require']['php'] ?? null; + + if (is_string($requiredVersion)) { + $composerPhpVersion = $requiredVersion; } } } + return $composerPhpVersion; } From e7d273301bf76ad29c1185d715c26c151bbd2c71 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 12 Nov 2024 17:20:50 +0100 Subject: [PATCH 2/4] Refactor ConstantResolver --- conf/config.neon | 1 - src/Analyser/ConstantResolver.php | 89 ++++++++++++++++++------ src/Analyser/ConstantResolverFactory.php | 4 +- src/Php/ComposerPhpVersionFactory.php | 30 -------- 4 files changed, 70 insertions(+), 54 deletions(-) diff --git a/conf/config.neon b/conf/config.neon index df5c15ddec..77b59a5d3b 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -345,7 +345,6 @@ services: - class: PHPStan\Php\ComposerPhpVersionFactory arguments: - phpVersion: %phpVersion% composerAutoloaderProjectPaths: %composerAutoloaderProjectPaths% - diff --git a/src/Analyser/ConstantResolver.php b/src/Analyser/ConstantResolver.php index 8176fe8abc..9f14e45992 100644 --- a/src/Analyser/ConstantResolver.php +++ b/src/Analyser/ConstantResolver.php @@ -3,10 +3,12 @@ namespace PHPStan\Analyser; use PhpParser\Node\Name; +use PHPStan\Php\ComposerPhpVersionFactory; use PHPStan\Php\PhpVersion; use PHPStan\Reflection\NamespaceAnswerer; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Reflection\ReflectionProvider\ReflectionProviderProvider; +use PHPStan\ShouldNotHappenException; use PHPStan\Type\Accessory\AccessoryNonFalsyStringType; use PHPStan\Type\Constant\ConstantFloatType; use PHPStan\Type\Constant\ConstantIntegerType; @@ -21,6 +23,8 @@ use PHPStan\Type\UnionType; use function array_key_exists; use function in_array; +use function is_array; +use function is_int; use function max; use function sprintf; use const INF; @@ -35,12 +39,13 @@ final class ConstantResolver /** * @param string[] $dynamicConstantNames + * @param int|array{min: int, max: int}|null $phpVersion */ public function __construct( private ReflectionProviderProvider $reflectionProviderProvider, private array $dynamicConstantNames, - private ?PhpVersion $composerMinPhpVersion, - private ?PhpVersion $composerMaxPhpVersion, + private int|array|null $phpVersion, + private ?ComposerPhpVersionFactory $composerPhpVersionFactory, ) { } @@ -87,11 +92,11 @@ public function resolvePredefinedConstant(string $resolvedConstantName): ?Type $minMajor = 5; $maxMajor = null; - if ($this->composerMinPhpVersion !== null) { - $minMajor = max($minMajor, $this->composerMinPhpVersion->getMajorVersionId()); + if ($this->getMinPhpVersion() !== null) { + $minMajor = max($minMajor, $this->getMinPhpVersion()->getMajorVersionId()); } - if ($this->composerMaxPhpVersion !== null) { - $maxMajor = $this->composerMaxPhpVersion->getMajorVersionId(); + if ($this->getMaxPhpVersion() !== null) { + $maxMajor = $this->getMaxPhpVersion()->getMajorVersionId(); } return $this->createInteger($minMajor, $maxMajor); @@ -101,12 +106,12 @@ public function resolvePredefinedConstant(string $resolvedConstantName): ?Type $maxMinor = null; if ( - $this->composerMinPhpVersion !== null - && $this->composerMaxPhpVersion !== null - && $this->composerMaxPhpVersion->getMajorVersionId() === $this->composerMinPhpVersion->getMajorVersionId() + $this->getMinPhpVersion() !== null + && $this->getMaxPhpVersion() !== null + && $this->getMaxPhpVersion()->getMajorVersionId() === $this->getMinPhpVersion()->getMajorVersionId() ) { - $minMinor = $this->composerMinPhpVersion->getMinorVersionId(); - $maxMinor = $this->composerMaxPhpVersion->getMinorVersionId(); + $minMinor = $this->getMinPhpVersion()->getMinorVersionId(); + $maxMinor = $this->getMaxPhpVersion()->getMinorVersionId(); } return $this->createInteger($minMinor, $maxMinor); @@ -116,13 +121,13 @@ public function resolvePredefinedConstant(string $resolvedConstantName): ?Type $maxRelease = null; if ( - $this->composerMinPhpVersion !== null - && $this->composerMaxPhpVersion !== null - && $this->composerMaxPhpVersion->getMajorVersionId() === $this->composerMinPhpVersion->getMajorVersionId() - && $this->composerMaxPhpVersion->getMinorVersionId() === $this->composerMinPhpVersion->getMinorVersionId() + $this->getMinPhpVersion() !== null + && $this->getMaxPhpVersion() !== null + && $this->getMaxPhpVersion()->getMajorVersionId() === $this->getMinPhpVersion()->getMajorVersionId() + && $this->getMaxPhpVersion()->getMinorVersionId() === $this->getMinPhpVersion()->getMinorVersionId() ) { - $minRelease = $this->composerMinPhpVersion->getPatchVersionId(); - $maxRelease = $this->composerMaxPhpVersion->getPatchVersionId(); + $minRelease = $this->getMinPhpVersion()->getPatchVersionId(); + $maxRelease = $this->getMaxPhpVersion()->getPatchVersionId(); } return $this->createInteger($minRelease, $maxRelease); @@ -130,11 +135,11 @@ public function resolvePredefinedConstant(string $resolvedConstantName): ?Type if ($resolvedConstantName === 'PHP_VERSION_ID') { $minVersion = 50207; $maxVersion = null; - if ($this->composerMinPhpVersion !== null) { - $minVersion = max($minVersion, $this->composerMinPhpVersion->getVersionId()); + if ($this->getMinPhpVersion() !== null) { + $minVersion = max($minVersion, $this->getMinPhpVersion()->getVersionId()); } - if ($this->composerMaxPhpVersion !== null) { - $maxVersion = $this->composerMaxPhpVersion->getVersionId(); + if ($this->getMaxPhpVersion() !== null) { + $maxVersion = $this->getMaxPhpVersion()->getVersionId(); } return $this->createInteger($minVersion, $maxVersion); @@ -351,6 +356,48 @@ public function resolvePredefinedConstant(string $resolvedConstantName): ?Type return null; } + private function getMinPhpVersion(): ?PhpVersion + { + if (is_int($this->phpVersion)) { + return null; + } + + if (is_array($this->phpVersion)) { + if ($this->phpVersion['max'] < $this->phpVersion['min']) { + throw new ShouldNotHappenException('Invalid PHP version range: phpVersion.max should be greater or equal to phpVersion.min.'); + } + + return new PhpVersion($this->phpVersion['min']); + } + + if ($this->composerPhpVersionFactory === null) { + return null; + } + + return $this->composerPhpVersionFactory->getMinVersion(); + } + + private function getMaxPhpVersion(): ?PhpVersion + { + if (is_int($this->phpVersion)) { + return null; + } + + if (is_array($this->phpVersion)) { + if ($this->phpVersion['max'] < $this->phpVersion['min']) { + throw new ShouldNotHappenException('Invalid PHP version range: phpVersion.max should be greater or equal to phpVersion.min.'); + } + + return new PhpVersion($this->phpVersion['max']); + } + + if ($this->composerPhpVersionFactory === null) { + return null; + } + + return $this->composerPhpVersionFactory->getMaxVersion(); + } + public function resolveConstantType(string $constantName, Type $constantType): Type { if ($constantType->isConstantValue()->yes() && in_array($constantName, $this->dynamicConstantNames, true)) { diff --git a/src/Analyser/ConstantResolverFactory.php b/src/Analyser/ConstantResolverFactory.php index f111da14ec..5ccc516e42 100644 --- a/src/Analyser/ConstantResolverFactory.php +++ b/src/Analyser/ConstantResolverFactory.php @@ -23,8 +23,8 @@ public function create(): ConstantResolver return new ConstantResolver( $this->reflectionProviderProvider, $this->container->getParameter('dynamicConstantNames'), - $composerFactory->getMinVersion(), - $composerFactory->getMaxVersion(), + $this->container->getParameter('phpVersion'), + $composerFactory, ); } diff --git a/src/Php/ComposerPhpVersionFactory.php b/src/Php/ComposerPhpVersionFactory.php index 81ac309dc6..88b81022ee 100644 --- a/src/Php/ComposerPhpVersionFactory.php +++ b/src/Php/ComposerPhpVersionFactory.php @@ -5,11 +5,8 @@ use Composer\Semver\VersionParser; use Nette\Utils\Strings; use PHPStan\Internal\ComposerHelper; -use PHPStan\ShouldNotHappenException; use function count; use function end; -use function is_array; -use function is_int; use function is_string; use function min; use function sprintf; @@ -25,11 +22,9 @@ final class ComposerPhpVersionFactory /** * @param string[] $composerAutoloaderProjectPaths - * @param int|array{min: int, max: int}|null $phpVersion */ public function __construct( private array $composerAutoloaderProjectPaths, - private int|array|null $phpVersion, ) { } @@ -38,23 +33,6 @@ private function initializeVersions(): void { $this->initialized = true; - $phpVersion = $this->phpVersion; - - if (is_int($phpVersion)) { - throw new ShouldNotHappenException(); - } - - if (is_array($phpVersion)) { - if ($phpVersion['max'] < $phpVersion['min']) { - throw new ShouldNotHappenException('Invalid PHP version range: phpVersion.max should be greater or equal to phpVersion.min.'); - } - - $this->minVersion = new PhpVersion($phpVersion['min']); - $this->maxVersion = new PhpVersion($phpVersion['max']); - - return; - } - // don't limit minVersion... PHPStan can analyze even PHP5 $this->maxVersion = new PhpVersion(PhpVersionFactory::MAX_PHP_VERSION); @@ -83,10 +61,6 @@ private function initializeVersions(): void public function getMinVersion(): ?PhpVersion { - if (is_int($this->phpVersion)) { - return null; - } - if ($this->initialized === false) { $this->initializeVersions(); } @@ -96,10 +70,6 @@ public function getMinVersion(): ?PhpVersion public function getMaxVersion(): ?PhpVersion { - if (is_int($this->phpVersion)) { - return null; - } - if ($this->initialized === false) { $this->initializeVersions(); } From 00a019c8abb4c8c7e3c5878975e13ab86b414bf8 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 12 Nov 2024 18:03:24 +0100 Subject: [PATCH 3/4] $composerPhpVersionFactory is not nullable --- src/Analyser/ConstantResolver.php | 10 +--------- .../ValidateIgnoredErrorsExtension.php | 4 +++- src/Testing/PHPStanTestCase.php | 4 +++- tests/PHPStan/Analyser/nsrt/bug-4434.php | 20 +++++++++---------- .../Analyser/nsrt/predefined-constants.php | 4 ++-- 5 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/Analyser/ConstantResolver.php b/src/Analyser/ConstantResolver.php index 9f14e45992..7a88b09c8d 100644 --- a/src/Analyser/ConstantResolver.php +++ b/src/Analyser/ConstantResolver.php @@ -45,7 +45,7 @@ public function __construct( private ReflectionProviderProvider $reflectionProviderProvider, private array $dynamicConstantNames, private int|array|null $phpVersion, - private ?ComposerPhpVersionFactory $composerPhpVersionFactory, + private ComposerPhpVersionFactory $composerPhpVersionFactory, ) { } @@ -370,10 +370,6 @@ private function getMinPhpVersion(): ?PhpVersion return new PhpVersion($this->phpVersion['min']); } - if ($this->composerPhpVersionFactory === null) { - return null; - } - return $this->composerPhpVersionFactory->getMinVersion(); } @@ -391,10 +387,6 @@ private function getMaxPhpVersion(): ?PhpVersion return new PhpVersion($this->phpVersion['max']); } - if ($this->composerPhpVersionFactory === null) { - return null; - } - return $this->composerPhpVersionFactory->getMaxVersion(); } diff --git a/src/DependencyInjection/ValidateIgnoredErrorsExtension.php b/src/DependencyInjection/ValidateIgnoredErrorsExtension.php index 92f0712e46..4560fde44c 100644 --- a/src/DependencyInjection/ValidateIgnoredErrorsExtension.php +++ b/src/DependencyInjection/ValidateIgnoredErrorsExtension.php @@ -12,6 +12,7 @@ use PHPStan\Command\IgnoredRegexValidator; use PHPStan\DependencyInjection\Type\OperatorTypeSpecifyingExtensionRegistryProvider; use PHPStan\File\FileExcluder; +use PHPStan\Php\ComposerPhpVersionFactory; use PHPStan\Php\PhpVersion; use PHPStan\PhpDoc\DirectTypeNodeResolverExtensionRegistryProvider; use PHPStan\PhpDoc\TypeNodeResolver; @@ -65,7 +66,8 @@ public function loadConfiguration(): void $reflectionProviderProvider = new DirectReflectionProviderProvider($reflectionProvider); ReflectionProviderStaticAccessor::registerInstance($reflectionProvider); PhpVersionStaticAccessor::registerInstance(new PhpVersion(PHP_VERSION_ID)); - $constantResolver = new ConstantResolver($reflectionProviderProvider, [], null, null); + $composerPhpVersionFactory = new ComposerPhpVersionFactory([]); + $constantResolver = new ConstantResolver($reflectionProviderProvider, [], null, $composerPhpVersionFactory); $phpDocParserConfig = new ParserConfig([]); $ignoredRegexValidator = new IgnoredRegexValidator( diff --git a/src/Testing/PHPStanTestCase.php b/src/Testing/PHPStanTestCase.php index aee824bfbc..31cdfadb78 100644 --- a/src/Testing/PHPStanTestCase.php +++ b/src/Testing/PHPStanTestCase.php @@ -21,6 +21,7 @@ use PHPStan\Internal\DirectoryCreatorException; use PHPStan\Node\Printer\ExprPrinter; use PHPStan\Parser\Parser; +use PHPStan\Php\ComposerPhpVersionFactory; use PHPStan\Php\PhpVersion; use PHPStan\PhpDoc\TypeNodeResolver; use PHPStan\PhpDoc\TypeStringResolver; @@ -137,7 +138,8 @@ public static function createScopeFactory(ReflectionProvider $reflectionProvider } $reflectionProviderProvider = new DirectReflectionProviderProvider($reflectionProvider); - $constantResolver = new ConstantResolver($reflectionProviderProvider, $dynamicConstantNames, null, null); + $composerPhpVersionFactory = $container->getByType(ComposerPhpVersionFactory::class); + $constantResolver = new ConstantResolver($reflectionProviderProvider, $dynamicConstantNames, null, $composerPhpVersionFactory); $initializerExprTypeResolver = new InitializerExprTypeResolver( $constantResolver, diff --git a/tests/PHPStan/Analyser/nsrt/bug-4434.php b/tests/PHPStan/Analyser/nsrt/bug-4434.php index a1f3bea048..7b48df20fd 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-4434.php +++ b/tests/PHPStan/Analyser/nsrt/bug-4434.php @@ -10,14 +10,14 @@ class HelloWorld public function testSendEmailToLog(): void { foreach ([1] as $emailFile) { - assertType('int<5, max>', PHP_MAJOR_VERSION); - assertType('int<5, max>', \PHP_MAJOR_VERSION); + assertType('int<5, 8>', PHP_MAJOR_VERSION); + assertType('int<5, 8>', \PHP_MAJOR_VERSION); if (PHP_MAJOR_VERSION === 7) { assertType('7', PHP_MAJOR_VERSION); assertType('7', \PHP_MAJOR_VERSION); } else { - assertType('int<5, 6>|int<8, max>', PHP_MAJOR_VERSION); - assertType('int<5, 6>|int<8, max>', \PHP_MAJOR_VERSION); + assertType('8|int<5, 6>', PHP_MAJOR_VERSION); + assertType('8|int<5, 6>', \PHP_MAJOR_VERSION); } } } @@ -28,14 +28,14 @@ class HelloWorld2 public function testSendEmailToLog(): void { foreach ([1] as $emailFile) { - assertType('int<5, max>', PHP_MAJOR_VERSION); - assertType('int<5, max>', \PHP_MAJOR_VERSION); + assertType('int<5, 8>', PHP_MAJOR_VERSION); + assertType('int<5, 8>', \PHP_MAJOR_VERSION); if (PHP_MAJOR_VERSION === 100) { - assertType('100', PHP_MAJOR_VERSION); - assertType('100', \PHP_MAJOR_VERSION); + assertType('*NEVER*', PHP_MAJOR_VERSION); + assertType('*NEVER*', \PHP_MAJOR_VERSION); } else { - assertType('int<5, 99>|int<101, max>', PHP_MAJOR_VERSION); - assertType('int<5, 99>|int<101, max>', \PHP_MAJOR_VERSION); + assertType('int<5, 8>', PHP_MAJOR_VERSION); + assertType('int<5, 8>', \PHP_MAJOR_VERSION); } } } diff --git a/tests/PHPStan/Analyser/nsrt/predefined-constants.php b/tests/PHPStan/Analyser/nsrt/predefined-constants.php index 70dceda653..9d9d1b1fc5 100644 --- a/tests/PHPStan/Analyser/nsrt/predefined-constants.php +++ b/tests/PHPStan/Analyser/nsrt/predefined-constants.php @@ -4,10 +4,10 @@ // core, https://www.php.net/manual/en/reserved.constants.php assertType('non-falsy-string', PHP_VERSION); -assertType('int<5, max>', PHP_MAJOR_VERSION); +assertType('int<5, 8>', PHP_MAJOR_VERSION); assertType('int<0, max>', PHP_MINOR_VERSION); assertType('int<0, max>', PHP_RELEASE_VERSION); -assertType('int<50207, max>', PHP_VERSION_ID); +assertType('int<50207, 80499>', PHP_VERSION_ID); assertType('string', PHP_EXTRA_VERSION); assertType('0|1', PHP_ZTS); assertType('0|1', PHP_DEBUG); From 73d0b1678cf0cc25db1ddc4276bc9afb47142d45 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 12 Nov 2024 18:07:16 +0100 Subject: [PATCH 4/4] prevent repetative getter calls --- src/Analyser/ConstantResolver.php | 46 ++++++++++++++++++------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/Analyser/ConstantResolver.php b/src/Analyser/ConstantResolver.php index 7a88b09c8d..846188b985 100644 --- a/src/Analyser/ConstantResolver.php +++ b/src/Analyser/ConstantResolver.php @@ -88,15 +88,23 @@ public function resolvePredefinedConstant(string $resolvedConstantName): ?Type new AccessoryNonFalsyStringType(), ]); } + + $minPhpVersion = null; + $maxPhpVersion = null; + if (in_array($resolvedConstantName, ['PHP_VERSION_ID', 'PHP_MAJOR_VERSION', 'PHP_MINOR_VERSION', 'PHP_RELEASE_VERSION'], true)) { + $minPhpVersion = $this->getMinPhpVersion(); + $maxPhpVersion = $this->getMaxPhpVersion(); + } + if ($resolvedConstantName === 'PHP_MAJOR_VERSION') { $minMajor = 5; $maxMajor = null; - if ($this->getMinPhpVersion() !== null) { - $minMajor = max($minMajor, $this->getMinPhpVersion()->getMajorVersionId()); + if ($minPhpVersion !== null) { + $minMajor = max($minMajor, $minPhpVersion->getMajorVersionId()); } - if ($this->getMaxPhpVersion() !== null) { - $maxMajor = $this->getMaxPhpVersion()->getMajorVersionId(); + if ($maxPhpVersion !== null) { + $maxMajor = $maxPhpVersion->getMajorVersionId(); } return $this->createInteger($minMajor, $maxMajor); @@ -106,12 +114,12 @@ public function resolvePredefinedConstant(string $resolvedConstantName): ?Type $maxMinor = null; if ( - $this->getMinPhpVersion() !== null - && $this->getMaxPhpVersion() !== null - && $this->getMaxPhpVersion()->getMajorVersionId() === $this->getMinPhpVersion()->getMajorVersionId() + $minPhpVersion !== null + && $maxPhpVersion !== null + && $maxPhpVersion->getMajorVersionId() === $minPhpVersion->getMajorVersionId() ) { - $minMinor = $this->getMinPhpVersion()->getMinorVersionId(); - $maxMinor = $this->getMaxPhpVersion()->getMinorVersionId(); + $minMinor = $minPhpVersion->getMinorVersionId(); + $maxMinor = $maxPhpVersion->getMinorVersionId(); } return $this->createInteger($minMinor, $maxMinor); @@ -121,13 +129,13 @@ public function resolvePredefinedConstant(string $resolvedConstantName): ?Type $maxRelease = null; if ( - $this->getMinPhpVersion() !== null - && $this->getMaxPhpVersion() !== null - && $this->getMaxPhpVersion()->getMajorVersionId() === $this->getMinPhpVersion()->getMajorVersionId() - && $this->getMaxPhpVersion()->getMinorVersionId() === $this->getMinPhpVersion()->getMinorVersionId() + $minPhpVersion !== null + && $maxPhpVersion !== null + && $maxPhpVersion->getMajorVersionId() === $minPhpVersion->getMajorVersionId() + && $maxPhpVersion->getMinorVersionId() === $minPhpVersion->getMinorVersionId() ) { - $minRelease = $this->getMinPhpVersion()->getPatchVersionId(); - $maxRelease = $this->getMaxPhpVersion()->getPatchVersionId(); + $minRelease = $minPhpVersion->getPatchVersionId(); + $maxRelease = $maxPhpVersion->getPatchVersionId(); } return $this->createInteger($minRelease, $maxRelease); @@ -135,11 +143,11 @@ public function resolvePredefinedConstant(string $resolvedConstantName): ?Type if ($resolvedConstantName === 'PHP_VERSION_ID') { $minVersion = 50207; $maxVersion = null; - if ($this->getMinPhpVersion() !== null) { - $minVersion = max($minVersion, $this->getMinPhpVersion()->getVersionId()); + if ($minPhpVersion !== null) { + $minVersion = max($minVersion, $minPhpVersion->getVersionId()); } - if ($this->getMaxPhpVersion() !== null) { - $maxVersion = $this->getMaxPhpVersion()->getVersionId(); + if ($maxPhpVersion !== null) { + $maxVersion = $maxPhpVersion->getVersionId(); } return $this->createInteger($minVersion, $maxVersion);