Skip to content

Reuse parsed PHPDocs of equal strings - #6732

Closed
SanderMuller wants to merge 1 commit into
phpstan:2.3.xfrom
SanderMuller:cache-parsed-phpdoc-strings
Closed

SanderMuller wants to merge 1 commit into
phpstan:2.3.xfrom
SanderMuller:cache-parsed-phpdoc-strings

Conversation

@SanderMuller

@SanderMuller SanderMuller commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

InvalidPhpDocTagValueRule and InvalidPHPStanDocTagRule each parse a node's doc comment with their own lexer and parser, one right after the other. FileTypeMapper parses many of the same strings through PhpDocStringResolver. This PR keeps the most recently parsed nodes in an LruCache in PhpDocStringResolver, and the two rules now use it. The new cache.parsedPhpDocCountMax parameter sets how many nodes it keeps, 1024 by default.

Nothing in src/ changes a parsed PhpDocNode, so equal strings can share one node. PhpDocEditor parses its own node and clones it before it edits. resolve() also consumes TOKEN_END, which the rules did not do. That cannot throw here, because a doc comment from php-parser ends at */.

Cold runs on 2.3.x at acd0511, one process (--debug), Turbo off, 3 interleaved runs per arm, PHP 8.5 on macOS arm64. The output was identical between the arms in every run.

before after
PHPStan's src/, level 6: instructions 658.7–658.8 G 651.5–652.1 G (−1.1%)
PHPStan's src/, level 6: CPU time 62.0–63.8 s 62.1–63.6 s
PHPStan's src/, level 6: memory_get_peak_usage() 576.6 MiB 573.5 MiB
a private Doctrine/Symfony application, level 5: instructions 777.6–779.2 G 776.3–776.6 G (−0.3%)
a private Doctrine/Symfony application, level 5: CPU time 70.8–73.4 s 71.2–72.7 s
a private Doctrine/Symfony application, level 5: memory_get_peak_usage() 616.1 MiB 620.7 MiB

The CPU time difference is within the noise. On the application, the cache keeps about 4.6 MiB more memory at peak. An earlier version of this PR, with the same 1024 limit, saved 2.6% of instructions on src/ and 0.6% on the application with Turbo on. I did not measure Turbo on again for this version.

PhpDocStringResolverTest::testEqualStringsShareOneNode checks that equal strings give the same node. It fails when the resolver does not store the node.

🤖 Generated with Claude Code

Comment thread src/PhpDoc/PhpDocStringResolver.php Outdated
private const PARSED_PHPDOC_CACHE_LIMIT = 1024;

/** @var array<string, PhpDocNode> least recently used first */
private array $parsedPhpDocs = [];

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.

Really nice find, but please use LruCache here, and also drop the testLeastRecentlyUsedNodeIsEvicted test.

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.

Also the limit should be configurable with similar to keys under the cache: section.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done. It uses LruCache now, the limit is cache.parsedPhpDocCountMax (1024 by default), and I removed testLeastRecentlyUsedNodeIsEvicted.

@staabm

staabm commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

would be great to rebase and re-measure this (memory and cpu time); and get it over the finish line

@SanderMuller

Copy link
Copy Markdown
Contributor Author

Sorry, saw these comments and then lost track of them, looking into it

FileTypeMapper, InvalidPhpDocTagValueRule and InvalidPHPStanDocTagRule
parse the same PHPDoc strings, and the two rules parse each one directly
after the other. PhpDocStringResolver now keeps the most recently parsed
nodes in an LruCache, and the two rules use it instead of their own lexer
and parser. The new cache.parsedPhpDocCountMax parameter sets how many
nodes it keeps, 1024 by default.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@SanderMuller
SanderMuller force-pushed the cache-parsed-phpdoc-strings branch from 70e8d11 to fbcde4b Compare October 24, 2026 13:22
@SanderMuller

Copy link
Copy Markdown
Contributor Author

Rebased and re-measured. The numbers are in the description.

@staabm

staabm commented Oct 24, 2026

Copy link
Copy Markdown
Contributor

judging by the numbers in the descriptions it seems this does not really improve memory or cpu time.

I will leave the decision wether its worth it to @ondrejmirtes

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.

3 participants