diff --git a/rules/coding-style/src/Rector/String_/UseClassKeywordForClassNameResolutionRector.php b/rules/coding-style/src/Rector/String_/UseClassKeywordForClassNameResolutionRector.php index ee26d3a7abb2..368301e28515 100644 --- a/rules/coding-style/src/Rector/String_/UseClassKeywordForClassNameResolutionRector.php +++ b/rules/coding-style/src/Rector/String_/UseClassKeywordForClassNameResolutionRector.php @@ -50,25 +50,52 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if (substr_count($node->value, '::') !== 1) { - return null; - } - // a possible static call reference - // @see https://regex101.com/r/Vv41Qr/1/ - [$before, $possibleClass, $after] = Strings::split($node->value, '#([\\\\a-zA-Z0-9_\\x80-\\xff]*)::#'); + $classNames = $this->getExistentClasses($node); - if (! $possibleClass || ! class_exists($possibleClass)) { - return null; + if ($classNames === []) { + return $node; } - $classConstFetch = new ClassConstFetch(new FullyQualified(ltrim($possibleClass, '\\')), 'class'); + $parts = $this->getParts($node, $classNames); + + foreach ($parts as $part) { + if (class_exists($part)) { + $returnNode = new ClassConstFetch(new FullyQualified(ltrim($part, '\\')), 'class'); + } else { + $returnNode = new String_($part); + } + + $concat = ! isset($concat) ? $returnNode : new Concat($concat, $returnNode); + } - $concat = new Concat($classConstFetch, new String_('::' . $after)); - if (! empty($before)) { - $concat = new Concat(new String_($before), $classConstFetch); - $concat = new Concat($concat, new String_('::' . $after)); + if (! isset($concat)) { + return $node; } return $concat; } + + public function getExistentClasses(String_ $string): array + { + // @see https://regex101.com/r/Vv41Qr/1/ + $matches = Strings::matchAll($string->value, '#([\\\\a-zA-Z0-9_\\x80-\\xff]*)::#', PREG_PATTERN_ORDER); + + return array_filter($matches[1], function (string $className) { + return class_exists($className); + }); + } + + public function getParts(String_ $string, array $classNames): array + { + $classNames = array_map(function (string $className) { + return preg_quote($className); + }, $classNames); + + // @see https://regex101.com/r/8nGS0F/1 + $parts = Strings::split($string->value, '#(' . implode('|', $classNames) . ')#'); + + return array_filter($parts, function (string $className) { + return $className !== ''; + }); + } } diff --git a/rules/coding-style/tests/Rector/String_/UseClassKeywordForClassNameResolutionRector/Fixture/mutiple_occurrences_in_one_string.php.inc b/rules/coding-style/tests/Rector/String_/UseClassKeywordForClassNameResolutionRector/Fixture/mutiple_occurrences_in_one_string.php.inc new file mode 100644 index 000000000000..e393a4e7e074 --- /dev/null +++ b/rules/coding-style/tests/Rector/String_/UseClassKeywordForClassNameResolutionRector/Fixture/mutiple_occurrences_in_one_string.php.inc @@ -0,0 +1,27 @@ + +----- +