diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 7cab09fb76d..a210457852b 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -237,23 +237,27 @@ jobs: - script: | cd e2e/composer-max-version composer install - ../../bin/phpstan analyze test.php + ../../bin/phpstan analyze test.php --level=0 - script: | cd e2e/composer-min-max-version composer install - ../../bin/phpstan analyze test.php + ../../bin/phpstan analyze test.php --level=0 - script: | cd e2e/composer-min-open-end-version composer install - ../../bin/phpstan analyze test.php + ../../bin/phpstan analyze test.php --level=0 + - script: | + cd e2e/composer-min-version-bc + composer install + ../../bin/phpstan analyze test.php --level=0 - script: | cd e2e/composer-min-version composer install - ../../bin/phpstan analyze test.php + ../../bin/phpstan analyze test.php --level=0 - script: | cd e2e/composer-version-config composer install - ../../bin/phpstan analyze test.php + ../../bin/phpstan analyze test.php --level=0 steps: - name: "Checkout" diff --git a/build/ignore-by-php-version.neon.php b/build/ignore-by-php-version.neon.php index 1b8669bfd50..cd049179a54 100644 --- a/build/ignore-by-php-version.neon.php +++ b/build/ignore-by-php-version.neon.php @@ -37,7 +37,5 @@ $config = []; $config['includes'] = $includes; $config['parameters']['phpVersion'] = PHP_VERSION_ID; -$config['parameters']['minPhpVersion'] = 70100; -$config['parameters']['maxPhpVersion'] = 90000; return $config; diff --git a/conf/config.neon b/conf/config.neon index 99b7cff2748..438ddbfaeeb 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -137,8 +137,6 @@ parameters: minimumNumberOfJobsPerProcess: 2 buffer: 134217728 # 128 MB phpVersion: null - minPhpVersion: null - maxPhpVersion: null polluteScopeWithLoopInitialAssignments: true polluteScopeWithAlwaysIterableForeach: true propertyAlwaysWrittenTags: [] @@ -376,15 +374,16 @@ services: - class: PHPStan\Php\PhpVersionFactoryFactory arguments: - versionId: %phpVersion% + phpVersion: %phpVersion% composerAutoloaderProjectPaths: %composerAutoloaderProjectPaths% + bleedingEdge: %featureToggles.bleedingEdge% - class: PHPStan\Php\ComposerPhpVersionFactory arguments: + phpVersion: %phpVersion% composerAutoloaderProjectPaths: %composerAutoloaderProjectPaths% - minVersion: %minPhpVersion% - maxVersion: %maxPhpVersion% + bleedingEdge: %featureToggles.bleedingEdge% - class: PHPStan\PhpDocParser\Lexer\Lexer diff --git a/conf/parametersSchema.neon b/conf/parametersSchema.neon index a839815bf4a..da69f3194b5 100644 --- a/conf/parametersSchema.neon +++ b/conf/parametersSchema.neon @@ -131,9 +131,13 @@ parametersSchema: minimumNumberOfJobsPerProcess: int(), buffer: int() ]) - phpVersion: schema(anyOf(schema(int(), min(70100), max(80399))), nullable()) - minPhpVersion: schema(anyOf(schema(int(), min(70100), max(90000))), nullable()) - maxPhpVersion: schema(anyOf(schema(int(), min(70100), max(90000))), nullable()) + phpVersion: schema(anyOf( + schema(int(), min(70100), max(80399)), + structure([ + min: schema(int(), min(70100), max(80399)), + max: schema(int(), min(70100), max(80399)) + ]) + ), nullable()) polluteScopeWithLoopInitialAssignments: bool() polluteScopeWithAlwaysIterableForeach: bool() propertyAlwaysWrittenTags: listOf(string()) diff --git a/e2e/composer-max-version/phpstan.neon b/e2e/composer-max-version/phpstan.neon new file mode 100644 index 00000000000..49d50a5baca --- /dev/null +++ b/e2e/composer-max-version/phpstan.neon @@ -0,0 +1,2 @@ +includes: + - ../../conf/bleedingEdge.neon diff --git a/e2e/composer-min-max-version/phpstan.neon b/e2e/composer-min-max-version/phpstan.neon new file mode 100644 index 00000000000..49d50a5baca --- /dev/null +++ b/e2e/composer-min-max-version/phpstan.neon @@ -0,0 +1,2 @@ +includes: + - ../../conf/bleedingEdge.neon diff --git a/e2e/composer-min-open-end-version/phpstan.neon b/e2e/composer-min-open-end-version/phpstan.neon new file mode 100644 index 00000000000..49d50a5baca --- /dev/null +++ b/e2e/composer-min-open-end-version/phpstan.neon @@ -0,0 +1,2 @@ +includes: + - ../../conf/bleedingEdge.neon diff --git a/e2e/composer-min-version-bc/.gitignore b/e2e/composer-min-version-bc/.gitignore new file mode 100644 index 00000000000..3a9875b460f --- /dev/null +++ b/e2e/composer-min-version-bc/.gitignore @@ -0,0 +1,2 @@ +/vendor/ +composer.lock diff --git a/e2e/composer-min-version-bc/composer.json b/e2e/composer-min-version-bc/composer.json new file mode 100644 index 00000000000..9be64619f16 --- /dev/null +++ b/e2e/composer-min-version-bc/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "php": "^8.1" + } +} diff --git a/e2e/composer-min-version-bc/test.php b/e2e/composer-min-version-bc/test.php new file mode 100644 index 00000000000..e377fff49e5 --- /dev/null +++ b/e2e/composer-min-version-bc/test.php @@ -0,0 +1,4 @@ +', PHP_VERSION_ID); diff --git a/e2e/composer-min-version/phpstan.neon b/e2e/composer-min-version/phpstan.neon new file mode 100644 index 00000000000..49d50a5baca --- /dev/null +++ b/e2e/composer-min-version/phpstan.neon @@ -0,0 +1,2 @@ +includes: + - ../../conf/bleedingEdge.neon diff --git a/e2e/composer-version-config/phpstan.neon b/e2e/composer-version-config/phpstan.neon index ffb53cebafb..003e5e1484e 100644 --- a/e2e/composer-version-config/phpstan.neon +++ b/e2e/composer-version-config/phpstan.neon @@ -1,4 +1,4 @@ parameters: - level: 0 - minPhpVersion: 80103 - maxPhpVersion: 80304 + phpVersion: + min: 80103 + max: 80304 diff --git a/src/Php/ComposerPhpVersionFactory.php b/src/Php/ComposerPhpVersionFactory.php index b63f7ee1581..9a845a190d4 100644 --- a/src/Php/ComposerPhpVersionFactory.php +++ b/src/Php/ComposerPhpVersionFactory.php @@ -8,9 +8,12 @@ use Nette\Utils\Strings; use PHPStan\File\CouldNotReadFileException; use PHPStan\File\FileReader; +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 sprintf; @@ -23,22 +26,31 @@ class ComposerPhpVersionFactory /** * @param string[] $composerAutoloaderProjectPaths + * @param int|array{min: int, max: int}|null $phpVersion */ public function __construct( private array $composerAutoloaderProjectPaths, - ?int $minVersion, - ?int $maxVersion, + int|array|null $phpVersion, + bool $bleedingEdge, ) { - if ($minVersion !== null) { - $this->minVersion = new PhpVersion($minVersion); + if (is_int($phpVersion)) { + return; } - if ($maxVersion !== null) { - $this->maxVersion = new PhpVersion($maxVersion); + + if (is_array($phpVersion)) { + if ($phpVersion['max'] < $phpVersion['min']) { + throw new ShouldNotHappenException('Invalid PHP version range: phpVersion.max should be great or equals to phpVersion.min.'); + } + + $this->minVersion = new PhpVersion($phpVersion['min']); + $this->maxVersion = new PhpVersion($phpVersion['max']); + + return; } - if ($minVersion !== null && $maxVersion !== null) { - return; // use values from config files + if (!$bleedingEdge) { + return; } // fallback to composer.json based php-version constraint diff --git a/src/Php/PhpVersionFactoryFactory.php b/src/Php/PhpVersionFactoryFactory.php index 870d1ff276d..cf8cefe868a 100644 --- a/src/Php/PhpVersionFactoryFactory.php +++ b/src/Php/PhpVersionFactoryFactory.php @@ -6,20 +6,26 @@ use Nette\Utils\JsonException; use PHPStan\File\CouldNotReadFileException; use PHPStan\File\FileReader; +use PHPStan\ShouldNotHappenException; +use function array_key_exists; use function count; use function end; +use function is_array; use function is_file; +use function is_int; use function is_string; class PhpVersionFactoryFactory { /** + * @param int|array{min: int, max: int}|null $phpVersion * @param string[] $composerAutoloaderProjectPaths */ public function __construct( - private ?int $versionId, + private int|array|null $phpVersion, private array $composerAutoloaderProjectPaths, + private bool $bleedingEdge, ) { } @@ -43,7 +49,20 @@ public function create(): PhpVersionFactory } } - return new PhpVersionFactory($this->versionId, $composerPhpVersion); + $versionId = null; + + if (is_int($this->phpVersion)) { + $versionId = $this->phpVersion; + } + + if ($this->bleedingEdge && is_array($this->phpVersion)) { + if (!array_key_exists('min', $this->phpVersion)) { + throw new ShouldNotHappenException(); + } + $versionId = $this->phpVersion['min']; + } + + return new PhpVersionFactory($versionId, $composerPhpVersion); } }