diff --git a/src/Configuration/SymbolsConfigurationFactory.php b/src/Configuration/SymbolsConfigurationFactory.php index bb9b1361..7ef3040e 100644 --- a/src/Configuration/SymbolsConfigurationFactory.php +++ b/src/Configuration/SymbolsConfigurationFactory.php @@ -25,6 +25,9 @@ use function is_bool; use function is_string; use function sprintf; +use function str_contains; +use function strrpos; +use function substr; final class SymbolsConfigurationFactory { @@ -203,12 +206,31 @@ private function getRegex(string $regex, string $key, int|string $index): string ); } - // TODO: double check that we are not adding it twice or that adding it twice does not break anything - $regex .= 'i'; + $flags = self::getRegexFlags($regex); + + if (!str_contains($flags, 'i')) { + // Ensure namespace comparisons are always case-insensitive + $regex .= 'i'; + } return $regex; } + /** + * @param non-empty-string $regex + */ + private static function getRegexFlags(string $regex): string + { + $separator = $regex[0]; + $lastSeparatorPosition = strrpos($regex, $separator); + + if (false === $lastSeparatorPosition) { + return ''; + } + + return substr($regex, $lastSeparatorPosition); + } + /** * @psalm-assert string[] $value */ @@ -240,6 +262,9 @@ private static function assertIsArrayOfStrings(mixed $value, string $key): void } } + /** + * @phpstan-assert non-empty-string $regex + */ private function assertValidRegex(string $regex, string $key, string $index): void { $errorMessage = $this->regexChecker->validateRegex($regex); diff --git a/tests/Configuration/SymbolsConfigurationFactoryTest.php b/tests/Configuration/SymbolsConfigurationFactoryTest.php index 918656f4..52c7c936 100644 --- a/tests/Configuration/SymbolsConfigurationFactoryTest.php +++ b/tests/Configuration/SymbolsConfigurationFactoryTest.php @@ -116,6 +116,48 @@ public static function configProvider(): iterable ), ]; + yield 'exclude namespace regex with flags' => [ + [ + ConfigurationKeys::EXCLUDE_NAMESPACES_KEYWORD => [ + '~^PHPUnit\\Runner(\\.*)?$~u', + ], + ], + SymbolsConfiguration::create( + excludedNamespaces: NamespaceRegistry::create( + [], + ['~^PHPUnit\\Runner(\\.*)?$~ui'], + ), + ), + ]; + + yield 'exclude namespace regex with case insensitive flag' => [ + [ + ConfigurationKeys::EXCLUDE_NAMESPACES_KEYWORD => [ + '~^PHPUnit\\Runner(\\.*)?$~i', + ], + ], + SymbolsConfiguration::create( + excludedNamespaces: NamespaceRegistry::create( + [], + ['~^PHPUnit\\Runner(\\.*)?$~i'], + ), + ), + ]; + + yield 'exclude namespace regex with several flags flag' => [ + [ + ConfigurationKeys::EXCLUDE_NAMESPACES_KEYWORD => [ + '~^PHPUnit\\Runner(\\.*)?$~uiA', + ], + ], + SymbolsConfiguration::create( + excludedNamespaces: NamespaceRegistry::create( + [], + ['~^PHPUnit\\Runner(\\.*)?$~uiA'], + ), + ), + ]; + yield 'nominal' => [ [ ConfigurationKeys::EXPOSE_GLOBAL_CONSTANTS_KEYWORD => false,