@@ -85,15 +85,17 @@ final class PregValidationRule implements Rule
8585 private ?ReDoSAnalyzer $ redosAnalyzer = null ;
8686
8787 /**
88- * @param bool $ignoreParseErrors Ignore parse errors for partial regex strings
89- * @param bool $reportRedos Report ReDoS vulnerability analysis
90- * @param string $redosThreshold Minimum ReDoS severity level to report
88+ * @param bool $ignoreParseErrors Ignore parse errors for partial regex strings
89+ * @param bool $reportRedos Report ReDoS vulnerability analysis
90+ * @param string $redosThreshold Minimum ReDoS severity level to report
91+ * @param array{digits: bool, word: bool} $optimizationConfig
9192 */
9293 public function __construct (
9394 private readonly bool $ ignoreParseErrors = true ,
9495 private readonly bool $ reportRedos = true ,
9596 private readonly string $ redosThreshold = 'high ' ,
9697 private readonly bool $ suggestOptimizations = false ,
98+ private readonly array $ optimizationConfig = ['digits ' => true , 'word ' => true ],
9799 ) {}
98100
99101 public function getNodeType (): string
@@ -262,7 +264,15 @@ private function validatePattern(string $pattern, int $lineNumber): array
262264
263265 if ($ this ->suggestOptimizations ) {
264266 try {
265- $ optimized = $ this ->getRegex ()->optimize ($ pattern );
267+ $ ast = $ this ->getRegex ()->parse ($ pattern );
268+ $ optimizer = new \RegexParser \NodeVisitor \OptimizerNodeVisitor (
269+ optimizeDigits: (bool ) ($ this ->optimizationConfig ['digits ' ] ?? true ),
270+ optimizeWord: (bool ) ($ this ->optimizationConfig ['word ' ] ?? true ),
271+ );
272+ $ optimizedAst = $ ast ->accept ($ optimizer );
273+ // Use compiler to get string back
274+ $ compiler = new \RegexParser \NodeVisitor \CompilerNodeVisitor ();
275+ $ optimized = $ optimizedAst ->accept ($ compiler );
266276 if ($ optimized !== $ pattern && \strlen ($ optimized ) < \strlen ($ pattern )) {
267277 // Safeguard: Validate that the optimized pattern is still valid
268278 try {
0 commit comments