2121use PHPStan \Type \StringType ;
2222use PHPStan \Type \Type ;
2323use PHPStan \Type \TypeCombinator ;
24+ use function array_key_exists ;
2425use function array_values ;
2526use function count ;
2627use function in_array ;
@@ -44,6 +45,9 @@ final class RegexGroupParser
4445
4546 private static ?Parser $ parser = null ;
4647
48+ /** @var array<string, ?TreeNode> */
49+ private static array $ parsedAst = [];
50+
4751 public function __construct (
4852 private PhpVersion $ phpVersion ,
4953 private RegexExpressionHelper $ regexExpressionHelper ,
@@ -56,30 +60,39 @@ public function parseGroups(string $regex): ?RegexAstWalkResult
5660 /** @throws void */
5761 self ::$ parser ??= Llk::load (new Read (__DIR__ . '/../../../resources/RegexGrammar.pp ' ));
5862
59- try {
60- Strings::match ('' , $ regex );
61- } catch (RegexpException ) {
62- // pattern is invalid, so let the RegularExpressionPatternRule report it
63- return null ;
64- }
65-
66- $ modifiers = $ this ->regexExpressionHelper ->getPatternModifiers ($ regex ) ?? '' ;
67- foreach (self ::NOT_SUPPORTED_MODIFIERS as $ notSupportedModifier ) {
68- if (str_contains ($ modifiers , $ notSupportedModifier )) {
63+ if (array_key_exists ($ regex , self ::$ parsedAst )) {
64+ $ ast = self ::$ parsedAst [$ regex ];
65+ if ($ ast === null ) {
6966 return null ;
7067 }
71- }
7268
73- if (str_contains ($ modifiers , 'x ' )) {
74- // in freespacing mode the # character starts a comment and runs until the end of the line
75- $ regex = preg_replace ('/(?<!\?)#.*/ ' , '' , $ regex ) ?? '' ;
76- }
69+ $ modifiers = $ this ->regexExpressionHelper ->getPatternModifiers ($ regex ) ?? '' ;
70+ } else {
71+ try {
72+ Strings::match ('' , $ regex );
73+ } catch (RegexpException ) {
74+ // pattern is invalid, so let the RegularExpressionPatternRule report it
75+ return self ::$ parsedAst [$ regex ] = null ;
76+ }
7777
78- $ rawRegex = $ this ->regexExpressionHelper ->removeDelimitersAndModifiers ($ regex );
79- try {
80- $ ast = self ::$ parser ->parse ($ rawRegex );
81- } catch (Exception ) {
82- return null ;
78+ $ modifiers = $ this ->regexExpressionHelper ->getPatternModifiers ($ regex ) ?? '' ;
79+ foreach (self ::NOT_SUPPORTED_MODIFIERS as $ notSupportedModifier ) {
80+ if (str_contains ($ modifiers , $ notSupportedModifier )) {
81+ return self ::$ parsedAst [$ regex ] = null ;
82+ }
83+ }
84+
85+ if (str_contains ($ modifiers , 'x ' )) {
86+ // in freespacing mode the # character starts a comment and runs until the end of the line
87+ $ regex = preg_replace ('/(?<!\?)#.*/ ' , '' , $ regex ) ?? '' ;
88+ }
89+
90+ $ rawRegex = $ this ->regexExpressionHelper ->removeDelimitersAndModifiers ($ regex );
91+ try {
92+ $ ast = self ::$ parsedAst [$ regex ] = self ::$ parser ->parse ($ rawRegex );
93+ } catch (Exception ) {
94+ return self ::$ parsedAst [$ regex ] = null ;
95+ }
8396 }
8497
8598 $ this ->updateAlternationAstRemoveVerticalBarsAndAddEmptyToken ($ ast );
0 commit comments