Skip to content

Use faster strspn() over regex matching - #7005

Merged
staabm merged 1 commit into
phpstan:2.3.xfrom
staabm:strspn
Nov 9, 2026
Merged

staabm merged 1 commit into
phpstan:2.3.xfrom
staabm:strspn

Conversation

@staabm

@staabm staabm commented Nov 8, 2026

Copy link
Copy Markdown
Contributor
➜  phpstan-src git:(strpbrk) ✗ php tests/bench/micro/trailing-whitespace.php                                 
PHP 8.5.12; PCRE JIT: 1; 200000 iterations, median of 5 rounds
Input                   Strings ns/op   strspn ns/op    speedup
empty                           294.8           18.3     16.12x
newline                         306.8           32.2      9.54x
whitespace                      319.3           34.4      9.29x
long whitespace                 388.7           90.3      4.31x
html                            292.9           31.5      9.29x
late non-whitespace             471.3           94.2      5.01x

@staabm
staabm requested a review from VincentLanglet November 8, 2026 08:51
@staabm

staabm commented Nov 8, 2026

Copy link
Copy Markdown
Contributor Author

//cc @SanderMuller

@staabm
staabm marked this pull request as ready for review November 8, 2026 12:16
@phpstan-bot

Copy link
Copy Markdown
Collaborator

This pull request has been marked as ready for review.

@SanderMuller SanderMuller left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The new check gives the same result as the old regex. I compared both on 201,281 strings. These were every byte value alone and next to a space or a newline. They also included random strings of up to 8 characters made from whitespace, \x85, \xA0, NUL and a letter. There was no difference, also with the UTF-8 ctype locale that the CLI sets.

There is one difference, and it is a fix. On 2,000,000 spaces followed by x, Strings::match() throws RegexpException: Backtrack limit was exhausted, with and without JIT. strspn() gives the correct result there.

The existing tests cover the change: 2 of the 5 rule tests fail when the new condition always skips. On the PR head, the rule tests, phpcs and make phpstan pass. The 4 red integration jobs are also red on #7007.

The effect on a full run is small. In self-analysis the rule checks 5,884 last nodes in 2,947 files, and only an InlineHTML node gets to strspn(). With the largest saving in your table, that is at most about 2 ms per run.

@staabm
staabm merged commit 6294159 into phpstan:2.3.x Nov 9, 2026
1606 of 1644 checks passed
@staabm
staabm deleted the strspn branch November 9, 2026 08:22
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.

4 participants