Skip to content

[Configuration] Auto-detect installed packages in withComposerBased() called with no argument - #8748

Open
MadBox-99 wants to merge 3 commits into
rectorphp:mainfrom
MadBox-99:composer-based-auto-detect
Open

MadBox-99 wants to merge 3 commits into
rectorphp:mainfrom
MadBox-99:composer-based-auto-detect

Conversation

@MadBox-99

@MadBox-99 MadBox-99 commented Oct 21, 2026 •

Copy link
Copy Markdown

withPhpSets() with no argument reads the PHP version from composer.json. withComposerBased() with no argument currently does nothing, so every project lists its packages by hand:

->withComposerBased(laravel: true, phpunit: true)

This PR makes the no-argument call pick the sets of the packages installed in the project:

->withComposerBased()

Passing any argument keeps today's behavior: only the passed sets are loaded, and detection is skipped. withComposerBased(phpunit: false) loads nothing, as before.

Detection

A set loads when any of its trigger packages is installed. InstalledPackageResolver reads the installed packages.

The trigger packages are listed in src/Configuration/ComposerBasedTriggerPackages.php. That file is generated by php scripts/generate-composer-based-trigger-packages.php, which reads every package the composer-based sets are bound to. It covers both rules that implement ComposerPackageConstraintInterface and configurations registered with ruleWithConfigurationComposerVersionBound(). Re-run the script when a set starts upgrading a new package.

argument trigger packages
symfony 57 packages: symfony/* components and bundles, plus jms/serializer and twig/twig
doctrine 9 packages: doctrine/orm, doctrine/dbal, doctrine/doctrine-bundle, doctrine/mongodb-odm, gedmo/doctrine-extensions, and others
twig twig/twig
phpunit phpunit/phpunit
laravel laravel/framework, illuminate/support (hardcoded in the script, as rector-laravel is not required here)
drupal drupal/core (hardcoded in the script, as drupal-rector is not required here)

Each rule in these sets is bound to its own package version, so a broad trigger is safe. A rule bound to a package that is not installed stays inactive.

BC

  • The signature is unchanged (bool ... = false). The no-argument path is detected with func_num_args() === 0.
  • A config calling withComposerBased() with no arguments loaded nothing before. Now it loads the detected sets, which is the intent of such a call.

Tests

tests/Configuration/ComposerBasedAutoDetectTest.php uses two fixture projects. One has doctrine/dbal, phpunit/phpunit and symfony/console installed; the other has none of the trigger packages. It covers four cases, and a fifth test checks that every withComposerBased() argument has an entry in the generated list:

  • With no argument, the PHPUnit, Doctrine and Symfony sets load: doctrine/dbal triggers Doctrine and symfony/console triggers Symfony.
  • With no argument and none of the trigger packages installed, no set loads.
  • An explicit twig: true loads only Twig.
  • An explicit phpunit: false loads nothing.

@samsonasik samsonasik left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think "framework" can be changed to "package" for generic wording

@TomasVotruba

Copy link
Copy Markdown
Member

I like the idea 👍

The Symfony/Laravel would have to be triggered by multiple packages though. E.g. symfony/http-kernel and symfony/console itself should load it. Look at https://github.com/rectorphp/rector-symfony/blob/main/config/sets/symfony/composer-based.php for inspiration.

@MadBox-99 MadBox-99 changed the title [Configuration] Auto-detect installed frameworks in withComposerBased() called with no argument [Configuration] Auto-detect installed packages in withComposerBased() called with no argument Oct 21, 2026
@MadBox-99

Copy link
Copy Markdown
Author

Thanks to both of you, updated in 995bbfe:

  • @TomasVotruba Each argument now has a list of trigger packages, and any one of them loads the set. Symfony is triggered by symfony/framework-bundle, symfony/http-kernel or symfony/console. Doctrine is triggered by doctrine/orm, doctrine/dbal or doctrine/doctrine-bundle. Laravel is triggered by laravel/framework or illuminate/support. The full table is in the description.
  • @samsonasik Renamed "framework" to "package" in the constant, the method, the docblocks and the PR title.

The test now uses a fixture project with doctrine/dbal and symfony/console, and no doctrine/orm or symfony/framework-bundle. A second fixture project has none of the trigger packages, and the test checks that it loads no set.

@MadBox-99

Copy link
Copy Markdown
Author

The failing "PHP 8.4 tests (ubuntu-latest)" job is not related to this PR. It is a race between two test classes that share one fixture directory.

StrStartsWithRectorTest and PolyfillRectorTest both read rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture. For each .php.inc fixture, AbstractRectorTestCase writes a temporary .php file next to it and deletes it in tearDown(). When fastunit puts the two classes into different parallel chunks, one class deletes the file while the other is still reading it:

hash_file(.../StrStartsWithRector/Fixture/skip_non_zero.php): Failed to open stream: No such file or directory

The first push passed this job, and nothing under rules-tests/ changed since. Could you re-run the failed job? I don't have the rights to re-run it.

'twig' => ['twig/twig'],
'doctrine' => ['doctrine/orm', 'doctrine/dbal', 'doctrine/doctrine-bundle'],
'phpunit' => ['phpunit/phpunit'],
'symfony' => ['symfony/framework-bundle', 'symfony/http-kernel', 'symfony/console'],

@TomasVotruba TomasVotruba Oct 21, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be 20-30 packages, check the linked set for more packages we upgrade.
Ideally build a script that extracts these values, and commit it here, so we have this list up-to-date.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in aa524fe. scripts/generate-composer-based-trigger-packages.php loads each composer-based set and collects the packages its rules are bound to. It covers both ComposerPackageConstraintInterface rules and ruleWithConfigurationComposerVersionBound() configurations, and writes them to src/Configuration/ComposerBasedTriggerPackages.php.

The result is 57 packages for Symfony and 9 for Doctrine. Laravel and Drupal stay hardcoded in the script, because rector-laravel and drupal-rector are not required in this repository. A test checks that every withComposerBased() argument has an entry in the generated list.

Should CI also run the script and fail when the generated list is out of date? I left that out because rector-symfony and rector-doctrine are pulled in as dev-main. Any change there would then fail unrelated PRs until someone regenerates the list. I'm happy to add it if you prefer.

@@ -0,0 +1,132 @@
<?php

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the script should be on github action with weekly run?

see https://github.com/rectorphp/rector-src/blob/main/.github/workflows/weekly_pull_requests.yaml

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can run it per commit, to fail if new package is there.
How fast it is?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run locally, seems fast enough: 0.253 seconds

➜  rector-src git:(composer-based-auto-detect) time php scripts/generate-composer-based-trigger-packages.php
twig: 1 packages
doctrine: 9 packages
phpunit: 1 packages
symfony: 56 packages
laravel: 2 packages
drupal: 1 packages

                                                                                                                        
 [OK] File "/Users/samsonasik/www/rector-src/src/Configuration/ComposerBasedTriggerPackages.php" was generated          
                                                                                                                        

0.19s user 0.03s system 87% cpu 0.253 total

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants