Skip to content

Fix quadratic parsing of PHPDocs with many generics - #326

Merged
ondrejmirtes merged 1 commit into
2.3.xfrom
faster-large-phpdocs
Oct 26, 2026
Merged

ondrejmirtes merged 1 commit into
2.3.xfrom
faster-large-phpdocs

Conversation

@ondrejmirtes

Copy link
Copy Markdown
Member

TypeParser::isHtml() runs for every Name< to tell a generic from an HTML tag such as <br>. For list<string>, the text <string> looks like a tag, so it walked all remaining tokens looking for </string>. That's one walk to the end of the PHPDoc per generic, so PHPDocs with many generics took quadratic time.

TokenIterator now records, once per PHPDoc, the last token containing each /name>. isHtml() returns early when no such text is written at or after the current token. Every end tag the walk looks for contains /name>, so the early return can't change a result. The walk itself is unchanged. The new TokenIterator::hasEndTagCandidateAhead() is @internal.

Parsing a PHPDoc with this line repeated N times: * @return array{a: int, b?: list<string>}|(callable(int): void)|null

Lines Before After
100 32 ms 3.2 ms
200 124 ms 6.4 ms
400 507 ms 13 ms

Typical PHPDocs are unchanged within noise: a corpus of 5371 real-world PHPDocs takes 92–97 ms on both versions.

Verification:

  • The ASTs are byte-identical to the previous parser on 10,742 corpus inputs (whole and truncated PHPDocs, with and without attributes).
  • They're also identical on 60,000 random PHPDocs mixing generics with <br>/</br>-style tags, end tags before and after the <, and broken tags.
  • make check passes.

TypeParser::isHtml() looked for the end tag of every "<name>" by walking the
rest of the tokens, so a PHPDoc with many generics like list<string> took
quadratic time. The iterator now remembers once where each "/name>" is
written, and the walk only runs when one is written ahead.

Amp-Thread-ID: https://ampcode.com/threads/T-01a17820-94d1-75c8-9206-2e802d9c0556
@ondrejmirtes
ondrejmirtes merged commit 3fcdcf0 into 2.3.x Oct 26, 2026
60 checks passed
@ondrejmirtes
ondrejmirtes deleted the faster-large-phpdocs branch October 26, 2026 17:24
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