From f89efdfa0bc9463ad7c99aa850648722baab491b Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 5 May 2026 13:56:22 +0200 Subject: [PATCH 1/2] PhpFileCleaner: Use strcspn instead of regex for fast-skip in clean() --- .../SourceLocator/PhpFileCleaner.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Reflection/BetterReflection/SourceLocator/PhpFileCleaner.php b/src/Reflection/BetterReflection/SourceLocator/PhpFileCleaner.php index 137941d0047..864f2fae836 100644 --- a/src/Reflection/BetterReflection/SourceLocator/PhpFileCleaner.php +++ b/src/Reflection/BetterReflection/SourceLocator/PhpFileCleaner.php @@ -7,6 +7,7 @@ use function in_array; use function preg_match; use function preg_quote; +use function strcspn; use function strlen; use function substr; @@ -20,7 +21,7 @@ final class PhpFileCleaner /** @var array */ private array $typeConfig = []; - private string $restPattern; + private string $rejectChars; private string $contents = ''; @@ -38,7 +39,7 @@ public function __construct() ]; } - $this->restPattern = '{[^{}?"\'typeConfig)) . ']+}A'; + $this->rejectChars = '{}?"\'typeConfig)); } public function clean(string $contents, int $maxMatches): string @@ -150,9 +151,10 @@ public function clean(string $contents, int $maxMatches): string } $this->index += 1; - if ($this->match($this->restPattern, $match)) { - $clean .= $char . $match[0]; - $this->index += strlen($match[0]); + $skip = strcspn($this->contents, $this->rejectChars, $this->index); + if ($skip > 0) { + $clean .= $char . substr($this->contents, $this->index, $skip); + $this->index += $skip; } else { $clean .= $char; } From 5b528a26fe7e463082bf2dc2c135f99a18853d90 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 5 May 2026 14:05:30 +0200 Subject: [PATCH 2/2] Apply strcspn / strspn optimization to the rest of PhpFileCleaner --- .../SourceLocator/PhpFileCleaner.php | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/Reflection/BetterReflection/SourceLocator/PhpFileCleaner.php b/src/Reflection/BetterReflection/SourceLocator/PhpFileCleaner.php index 864f2fae836..955a9ecdd66 100644 --- a/src/Reflection/BetterReflection/SourceLocator/PhpFileCleaner.php +++ b/src/Reflection/BetterReflection/SourceLocator/PhpFileCleaner.php @@ -9,6 +9,7 @@ use function preg_quote; use function strcspn; use function strlen; +use function strspn; use function substr; /** @@ -205,8 +206,13 @@ private function consumeString(string $delimiter): string private function skipString(string $delimiter): void { + $rejectChars = '\\' . $delimiter; $this->index += 1; while ($this->index < $this->len) { + $this->index += strcspn($this->contents, $rejectChars, $this->index); + if ($this->index >= $this->len) { + break; + } if ($this->contents[$this->index] === '\\' && ($this->peek('\\') || $this->peek($delimiter))) { $this->index += 2; continue; @@ -223,7 +229,9 @@ private function skipComment(): void { $this->index += 2; while ($this->index < $this->len) { - if ($this->contents[$this->index] === '*' && $this->peek('/')) { + $this->index += strcspn($this->contents, '*', $this->index); + + if ($this->peek('/')) { $this->index += 2; break; } @@ -234,12 +242,7 @@ private function skipComment(): void private function skipToNewline(): void { - while ($this->index < $this->len) { - if (in_array($this->contents[$this->index], ["\r", "\n"], true)) { - return; - } - $this->index += 1; - } + $this->index += strcspn($this->contents, "\r\n", $this->index); } private function skipHeredoc(string $delimiter): void @@ -267,16 +270,10 @@ private function skipHeredoc(string $delimiter): void } // skip the rest of the line - while ($this->index < $this->len) { - $this->skipToNewline(); - - // skip newlines - while ($this->index < $this->len && ($this->contents[$this->index] === "\r" || $this->contents[$this->index] === "\n")) { - $this->index += 1; - } + $this->skipToNewline(); - break; - } + // skip newlines + $this->index += strspn($this->contents, "\r\n", $this->index); } }