Perf: cache docblock FQCN lookups#65
Merged
Merged
Conversation
dereuromark
added a commit
that referenced
this pull request
May 13, 2026
UseStatementSniff has its own getUseStatements() implementation that duplicates UseStatementsTrait::getUseStatements() (cached in #64). It already has an instance-level cache via existingStatements, which covers repeated calls within a single phpcs pass; the new static cache adds coverage across phpcbf fix iterations, where populateTokenListeners() creates fresh sniff instances per pass and resets the instance cache. Cache invalidation follows the same fingerprint-based scheme as #64: token count alone is not strong enough, since an alias rename keeps it constant. Cached entries record a content fingerprint of each use statement range and re-verify them against the live tokens before being trusted. The cache also refuses to serve an empty result so it cannot return stale state for a file where a fix added a first use statement while another simultaneous fix happened to keep the file's overall token count unchanged. Measured on the same CakePHP 5 app from #62 / #63 / #64 / #65 (parallel=1, --report=performance): PhpCollective.Namespaces.UseStatement 3.36s -> 2.08s Existing test suite (100 tests / 122 assertions) passes unchanged. The FQCN cache changes from the earlier draft of this PR were dropped in favour of the cache that landed via #65.
dereuromark
added a commit
that referenced
this pull request
May 13, 2026
…#66) UseStatementSniff has its own getUseStatements() implementation that duplicates UseStatementsTrait::getUseStatements() (cached in #64). It already has an instance-level cache via existingStatements, which covers repeated calls within a single phpcs pass; the new static cache adds coverage across phpcbf fix iterations, where populateTokenListeners() creates fresh sniff instances per pass and resets the instance cache. Cache invalidation follows the same fingerprint-based scheme as #64: token count alone is not strong enough, since an alias rename keeps it constant. Cached entries record a content fingerprint of each use statement range and re-verify them against the live tokens before being trusted. The cache also refuses to serve an empty result so it cannot return stale state for a file where a fix added a first use statement while another simultaneous fix happened to keep the file's overall token count unchanged. Measured on the same CakePHP 5 app from #62 / #63 / #64 / #65 (parallel=1, --report=performance): PhpCollective.Namespaces.UseStatement 3.36s -> 2.08s Existing test suite (100 tests / 122 assertions) passes unchanged. The FQCN cache changes from the earlier draft of this PR were dropped in favour of the cache that landed via #65.
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.
Summary
Cache file-level work in
FullyQualifiedClassNameInDocBlockSniffand avoid reprocessing the same doc block multiple times in a single phpcs pass.This does two things:
T_CLASS,T_INTERFACE,T_TRAIT,T_FUNCTION,T_VARIABLE,T_COMMENT), so the same doc block is only parsed once per pass;usestatements for the lifetime of that pass instead of rebuilding them for every class-name lookup.The cache is pass-scoped, not content-scoped: it resets when token processing restarts from an earlier stack pointer, which is what phpcbf does after re-tokenizing a file for a new fix loop.
Why
After #63 and #64 landed,
PhpCollective.Commenting.FullyQualifiedClassNameInDocBlockbecame the dominant repo-local hotspot on large docblock-heavy files.The old code had two expensive patterns:
parseUseStatements()walked the whole file every timefindUseStatementForClassName()was called;That meant a large method-heavy file with repeated
@param/@return/@throwstags paid for the same file-level scans many times.Behavior
This also removes duplicate error reporting for the same doc block. The existing fixture still fixes to the same output, but the unique violation count drops from
11to7, which is what the updated test now asserts.Benchmark
Measured on a synthetic large file with 430 repeated methods and docblocks:
PhpCollective.Commenting.FullyQualifiedClassNameInDocBlockabout49.99sPhpCollective.Commenting.FullyQualifiedClassNameInDocBlockabout1.35sVerification
composer cs-fixcomposer testcomposer stan