[Composer] Target lowest declared version for libraries in composer-based sets - #8359
Open
TomasVotruba wants to merge 1 commit into
Open
[Composer] Target lowest declared version for libraries in composer-based sets#8359TomasVotruba wants to merge 1 commit into
TomasVotruba wants to merge 1 commit into
Conversation
…ased sets A library declares a compatibility range in composer.json (e.g. "^10.5 || ^11.0 || ^12.0"). Composer-based rule filtering used the locally installed version, so an installed PHPUnit 12 made Rector emit 12-only code and silently break the declared support for 10.5. When composer.json has "type": "library", derive the target version from the lowest declared constraint instead of the installed one, making the output deterministic across composer install/update and --prefer-lowest. Refs rectorphp/rector#9858
samsonasik
reviewed
Aug 22, 2026
| private function createInstalledPackages(array $packages): array | ||
| { | ||
| $packageConstraints = $this->resolvePackageConstraints(); | ||
| $isLibrary = $this->isLibrary(); |
Member
There was a problem hiding this comment.
I think library check is not needed, even on project, eg on "framework skeleton", phpunit range may exists to give user ability to use phpunit version based on specific php version.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes deterministic version targeting for libraries in composer-based sets. Refs rectorphp/rector#9858.
Problem
withComposerBased()rules gate on the locally installed package version (vendor/composer/installed.json). That is right for applications, wrong for libraries.A library declares a compatibility range in
composer.json:{ "type": "library", "require": { "phpunit/phpunit": "^10.5 || ^11.0 || ^12.0" } }With PHPUnit 12 installed locally, Rector emitted 12-only code and silently broke the declared support for 10.5. Output changed between
composer install,composer update, andcomposer update --prefer-lowestfor identical source.Fix
When
composer.jsonhas"type": "library", derive the target version from the lowest declared constraint instead of the installed version:$constraint = $packageConstraints[$name] ?? null; if (is_string($constraint)) { - // the "installed.json" can be outdated, e.g. after a branch switch; - // in such case the "composer.json" constraint has a priority - $version = $this->matchConstraintVersion($version, $constraint) ?? $version; + if ($isLibrary) { + // a library must stay compatible with the lowest version it declares, + // regardless of which one happens to be installed locally + $version = $this->resolveConstraintLowestVersion($constraint) ?? $version; + } else { + // the "installed.json" can be outdated, e.g. after a branch switch; + // in such case the "composer.json" constraint has a priority + $version = $this->matchConstraintVersion($version, $constraint) ?? $version; + } }Applications (no
type, ortypeother thanlibrary) keep the current installed-version behavior. The detection is explicit: only"type": "library"opts in.Effect
phpunit ^10.5 || ^11 || ^12phpunit ^10.5 || ^11 || ^12symfony/console ^7.0Output is now deterministic for libraries across
install/update/--prefer-lowest.