Repository navigation
[perf] Avoid re-parsing current file in RemoveUnusedPrivateClassConstantRector - #8644
Merged
TomasVotruba merged 3 commits intoOct 12, 2026
Conversation
…antRector ClassConstManipulator::hasClassConstFetch() looks for constant fetches in every class ancestor resolved through AstResolver. ClassReflection::getAncestors() includes the class itself, so for each file with a private constant, the file being processed was parsed again and its PHPStan scope fully resolved again. The rule now subscribes to Class_/Enum_ and passes the in-memory class node, which is searched instead of the re-parsed one. Unused constants are removed only after all constants are checked, so the result does not depend on their order.
A private constant cannot be fetched from a parent class or an interface, so only the traits still need to be resolved through AstResolver.
Member
|
Thanks, lets ship it 👍 |
guillaume-sainthillier
deleted the
feat/rector-benchmark-optimize-a870ca
branch
October 12, 2026 12:05
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.
Problem
ClassConstManipulator::hasClassConstFetch()searches for constant fetches in every ancestor of the class, resolved throughAstResolver::resolveClassFromClassReflection().ClassReflection::getAncestors()includes the class itself. So for every file that has a private constant, the file currently being processed was parsed again and its PHPStan scope fully resolved again, only to look forself::FOOin a class Rector already has in memory. TheAstResolvercache doesn't help, because each file is processed once.In a profile of Rector running its own
rector.phpon part ofsrc/, this single call path accounted for ~16% of total runtime.Change
ClassConstManipulator::hasClassConstFetch()now requires theClassLikeowning the constant, and searches only that in-memory class and the traits it uses. Parents and interfaces are no longer resolved, as a private constant cannot be fetched there.AstResolveris kept only for traits, since a trait method runs in the using class scope (covered byskip_used_in_trait.php.inc).RemoveUnusedPrivateClassConstantRectornow subscribes toClass_/Enum_so it can pass the class node. Unused constants are collected first and removed afterwards, so the result doesn't depend on declaration order (as before, when the search ran on the unchanged file from disk).Benchmarks
Single process,
--dry-run --clear-cache, local machine:rector.php(all prepared sets) onsrc/(510 files)Behavior
changesentry points to the class line, with one entry per class instead of one per constant.