Skip to content

[perf] Speed up RemoveUnusedPrivateMethodRector with early data provider and class-string call checks and deduplicated scope resolution - #8800

Open
alfredbez wants to merge 1 commit into
rectorphp:mainfrom
alfredbez:fix/rector-perf-remove-unused-private-method-guard
Open

alfredbez wants to merge 1 commit into
rectorphp:mainfrom
alfredbez:fix/rector-perf-remove-unused-private-method-guard

Conversation

@alfredbez

Copy link
Copy Markdown
Contributor

In RemoveUnusedPrivateMethodRector, IsClassMethodUsedAnalyzer::isClassMethodUsed() (which traverses the class AST) was invoked for private methods before checking whether the method is a PHPUnit data provider or target of a class-string static call (self::class::method()). In addition, ScopeFetcher::fetch($node) was called repeatedly on the same Class_ node for each private method.

Reproducer

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

final class Subject extends TestCase
{
    #[DataProvider('provideData')]
    public function testSomething(int $value): void
    {
        self::assertSame($value, self::class::computeExpected());
    }

    private static function provideData(): array
    {
        return [[1]];
    }

    private static function computeExpected(): int
    {
        return 1;
    }
}

Expected Behavior

RemoveUnusedPrivateMethodRector should check known-used method name lists (dataProviderMethodNames and classStringCallMethodNames) before running AST usage analysis, and fetch the class scope lazily at most once per class node.

@alfredbez
alfredbez force-pushed the fix/rector-perf-remove-unused-private-method-guard branch from 407339b to 3cea0fa Compare October 23, 2026 23:38
@TomasVotruba

TomasVotruba commented Oct 24, 2026 •

Copy link
Copy Markdown
Member

Reorder is behavior-neutral and the win looks real (name-list checks now gate isClassMethodUsed(), which traverses the whole class AST, plus scope fetched once per class instead of per method).

Could you add a quick before/after number though? This is the only one of the three perf PRs touching the hot traversal path, yet it ships no bench. A small bench/*.php micro-script (LazyContainerFactory + TestingParser, getrusage CPU, warmup excluded) on both branches would lock it in - same shape as the one in #8801.

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.

2 participants