Skip to content

Reuse the errors of rules on CollectedDataNode from the result cache - #6840

Open
SanderMuller wants to merge 2 commits into
phpstan:2.3.xfrom
SanderMuller:cache-collected-data-rule-errors
Open

SanderMuller wants to merge 2 commits into
phpstan:2.3.xfrom
SanderMuller:cache-collected-data-rule-errors

Conversation

@SanderMuller

Copy link
Copy Markdown
Contributor

This follows the design from phpstan/phpstan#15447 (comment).

The rules on CollectedDataNode now run again only when their input changed. CollectedDataNode records which collectors a rule reads with get(), and whether it calls isOnlyFilesAnalysis(). The result cache stores the errors of each rule with a hash of that data. On the next run, a rule with the same input gets its stored errors back.

The ignore extensions and the local ignores still run on every error, also on reused ones. A rule that throws is not stored, so it runs every time. Each rule gets its own CollectedDataNode, so a service that memoizes per node cannot hide a read from the next rule.

To store the rule results, the result cache is now saved after the finalizer ran. That is the first commit.

PHPStan self-analysis with dead-code-detector (build/phpstan.neon, 13 errors in both arms):

2.3.x this PR
warm, nothing changed 1.42 s (CPU 1.10 s) 0.45 s (CPU 0.40 s)
warm, one string literal changed 1.83 s (CPU 1.51 s) 0.86 s (CPU 0.77 s)
cold 15.13 s (CPU 110.1 s) 15.15 s (CPU 111.5 s)

Warm rows are the median of 5 rounds, cold of 9 rounds in both orders. The result cache grows from 39.4 MB to 39.7 MB.

A rule can also depend on data the node does not see. larastan's UnusedViewsRule reads the Blade view files from disk. A new unused view does not change the collected data, so this PR can return the old errors. I did not run larastan with this PR. Should the reuse be opt-in for third-party rules, or opt-out with an interface or attribute? I can change it either way.

This overlaps with #6373, which loads the cached collected data only when the rules run. This PR hashes the collected data on every warm run, so it reads it every time.

Refs phpstan/phpstan#15447

🤖 Generated with Claude Code

SanderMuller and others added 2 commits October 25, 2026 21:06
ResultCacheManager::process() now returns the save as a callback, and both callers call it after
AnalyserResultFinalizer::finalize(). Nothing else changes: the same data is saved.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
CollectedDataNode records which collectors a rule reads and whether it
reads isOnlyFilesAnalysis(). The result cache stores the errors of each
rule with a hash of that input. On the next run, a rule whose input is
the same returns its stored errors and does not run again.

The ignore extensions and the local ignores still run on every error,
also on reused errors.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@SanderMuller
SanderMuller force-pushed the cache-collected-data-rule-errors branch from b36b4f2 to b6b5518 Compare October 25, 2026 20:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant