diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php index acc9b1ca5cd..d6820746969 100644 --- a/.phpstorm.meta.php +++ b/.phpstorm.meta.php @@ -48,7 +48,6 @@ 0, \Rector\NodeTypeResolver\Node\AttributeKey::SCOPE, \Rector\NodeTypeResolver\Node\AttributeKey::REPRINT_RAW_VALUE, - \Rector\NodeTypeResolver\Node\AttributeKey::PARENT_NODE, \Rector\NodeTypeResolver\Node\AttributeKey::ORIGINAL_NODE, \Rector\NodeTypeResolver\Node\AttributeKey::IS_UNREACHABLE, \Rector\NodeTypeResolver\Node\AttributeKey::PHP_DOC_INFO, @@ -67,7 +66,6 @@ 0, \Rector\NodeTypeResolver\Node\AttributeKey::SCOPE, \Rector\NodeTypeResolver\Node\AttributeKey::REPRINT_RAW_VALUE, - \Rector\NodeTypeResolver\Node\AttributeKey::PARENT_NODE, \Rector\NodeTypeResolver\Node\AttributeKey::ORIGINAL_NODE, \Rector\NodeTypeResolver\Node\AttributeKey::IS_UNREACHABLE, \Rector\NodeTypeResolver\Node\AttributeKey::PHP_DOC_INFO, diff --git a/phpstan.neon b/phpstan.neon index abc38e1f425..9b43a8a3412 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -725,3 +725,7 @@ parameters: - message: '#Offset 0 does not exist on array\|null#' path: rules/Php70/Rector/ClassMethod/Php4ConstructorRector.php + + # resolve before Rector 1.0 + - '#Call to deprecated method findParentType#' + - '#Call to deprecated method findParentByTypes#' diff --git a/rector.php b/rector.php index f8432d4afa3..3157fb2986b 100644 --- a/rector.php +++ b/rector.php @@ -83,6 +83,11 @@ __DIR__ . '/packages/Testing/PHPUnit/Behavior/MovingFilesTrait.php', ], + // avoid simplifying itself + \Rector\CodeQuality\Rector\FuncCall\SimplifyRegexPatternRector::class => [ + __DIR__ . '/rules/CodeQuality/Rector/FuncCall/SimplifyRegexPatternRector.php', + ], + // race condition with stmts aware patch and PHPStan type \Rector\TypeDeclaration\Rector\ClassMethod\AddMethodCallBasedStrictParamTypeRector::class => [ __DIR__ . '/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php', diff --git a/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/Fixture/skip_heredoc.php.inc b/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/Fixture/skip_heredoc.php.inc new file mode 100644 index 00000000000..bf15ad06d13 --- /dev/null +++ b/rules-tests/Php73/Rector/FuncCall/RegexDashEscapeRector/Fixture/skip_heredoc.php.inc @@ -0,0 +1,10 @@ + '\s', ]; - public function __construct( - private readonly RegexPatternArgumentManipulator $regexPatternArgumentManipulator - ) { - } - public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Simplify regex pattern to known ranges', [ @@ -69,40 +62,27 @@ public function run($value) */ public function getNodeTypes(): array { - return [FuncCall::class, StaticCall::class]; + return [String_::class]; } /** - * @param FuncCall|StaticCall $node + * @param String_ $node */ public function refactor(Node $node): ?Node { - $patterns = $this->regexPatternArgumentManipulator->matchCallArgumentWithRegexPattern($node); - if ($patterns === []) { - return null; - } - - $hasChanged = false; + foreach (self::COMPLEX_PATTERN_TO_SIMPLE as $complexPattern => $simple) { + $originalValue = $node->value; + $simplifiedValue = Strings::replace( + $node->value, + '#' . preg_quote($complexPattern, '#') . '#', + $simple + ); - foreach ($patterns as $pattern) { - foreach (self::COMPLEX_PATTERN_TO_SIMPLE as $complexPattern => $simple) { - $originalValue = $pattern->value; - $simplifiedValue = Strings::replace( - $pattern->value, - '#' . preg_quote($complexPattern, '#') . '#', - $simple - ); - - if ($originalValue === $simplifiedValue) { - continue; - } - - $pattern->value = $simplifiedValue; - $hasChanged = true; + if ($originalValue === $simplifiedValue) { + continue; } - } - if ($hasChanged) { + $node->value = $simplifiedValue; return $node; } diff --git a/rules/Php73/Rector/FuncCall/RegexDashEscapeRector.php b/rules/Php73/Rector/FuncCall/RegexDashEscapeRector.php index b71e1baaada..228eb35006a 100644 --- a/rules/Php73/Rector/FuncCall/RegexDashEscapeRector.php +++ b/rules/Php73/Rector/FuncCall/RegexDashEscapeRector.php @@ -6,10 +6,7 @@ use Nette\Utils\Strings; use PhpParser\Node; -use PhpParser\Node\Expr\FuncCall; -use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Scalar\String_; -use Rector\Core\Php\Regex\RegexPatternArgumentManipulator; use Rector\Core\Rector\AbstractRector; use Rector\Core\Util\StringUtils; use Rector\Core\ValueObject\PhpVersionFeature; @@ -44,13 +41,6 @@ final class RegexDashEscapeRector extends AbstractRector implements MinPhpVersio */ private const RIGHT_HAND_UNESCAPED_DASH_REGEX = '#(?regexPatternArgumentManipulator->matchCallArgumentWithRegexPattern($node); - if ($regexArguments === []) { + $stringKind = $node->getAttribute(AttributeKey::KIND); + if (in_array($stringKind, [String_::KIND_HEREDOC, String_::KIND_NOWDOC], true)) { return null; } - foreach ($regexArguments as $regexArgument) { - if (StringUtils::isMatch($regexArgument->value, self::THREE_BACKSLASH_FOR_ESCAPE_NEXT_REGEX)) { - continue; - } - - $this->escapeStringNode($regexArgument); - } - - if (! $this->hasChanged) { + if (StringUtils::isMatch($node->value, self::THREE_BACKSLASH_FOR_ESCAPE_NEXT_REGEX)) { return null; } - return $node; - } - - private function escapeStringNode(String_ $string): void - { - $stringValue = $string->value; + $stringValue = $node->value; if (StringUtils::isMatch($stringValue, self::LEFT_HAND_UNESCAPED_DASH_REGEX)) { - $string->value = Strings::replace($stringValue, self::LEFT_HAND_UNESCAPED_DASH_REGEX, '$1\-'); + $node->value = Strings::replace($stringValue, self::LEFT_HAND_UNESCAPED_DASH_REGEX, '$1\-'); // helped needed to skip re-escaping regular expression - $string->setAttribute(AttributeKey::IS_REGULAR_PATTERN, true); - $this->hasChanged = true; - return; + $node->setAttribute(AttributeKey::IS_REGULAR_PATTERN, true); + + return $node; } if (StringUtils::isMatch($stringValue, self::RIGHT_HAND_UNESCAPED_DASH_REGEX)) { - $string->value = Strings::replace($stringValue, self::RIGHT_HAND_UNESCAPED_DASH_REGEX, '\-$1]'); + $node->value = Strings::replace($stringValue, self::RIGHT_HAND_UNESCAPED_DASH_REGEX, '\-$1]'); // helped needed to skip re-escaping regular expression - $string->setAttribute(AttributeKey::IS_REGULAR_PATTERN, true); - $this->hasChanged = true; + $node->setAttribute(AttributeKey::IS_REGULAR_PATTERN, true); + + return $node; } + + return null; } } diff --git a/src/Kernel/RectorKernel.php b/src/Kernel/RectorKernel.php index 2cb58d6805e..7bcbcdf52b6 100644 --- a/src/Kernel/RectorKernel.php +++ b/src/Kernel/RectorKernel.php @@ -17,7 +17,7 @@ final class RectorKernel /** * @var string */ - private const CACHE_KEY = 'v67'; + private const CACHE_KEY = 'v68'; private ContainerInterface|null $container = null; diff --git a/src/Php/Regex/RegexPatternArgumentManipulator.php b/src/Php/Regex/RegexPatternArgumentManipulator.php deleted file mode 100644 index 1eb06b1a6b9..00000000000 --- a/src/Php/Regex/RegexPatternArgumentManipulator.php +++ /dev/null @@ -1,191 +0,0 @@ - - */ - private const FUNCTIONS_WITH_PATTERNS_TO_ARGUMENT_POSITION = [ - 'preg_match' => 0, - 'preg_replace_callback_array' => 0, - 'preg_replace_callback' => 0, - 'preg_replace' => 0, - 'preg_match_all' => 0, - 'preg_split' => 0, - 'preg_grep' => 0, - ]; - - /** - * @var array> - */ - private const STATIC_METHODS_WITH_PATTERNS_TO_ARGUMENT_POSITION = [ - Strings::class => [ - 'match' => 1, - 'matchAll' => 1, - 'replace' => 1, - 'split' => 1, - ], - ]; - - public function __construct( - private readonly BetterNodeFinder $betterNodeFinder, - private readonly NodeNameResolver $nodeNameResolver, - private readonly NodeTypeResolver $nodeTypeResolver, - private readonly LocalConstantFinder $localConstantFinder, - private readonly NodeComparator $nodeComparator - ) { - } - - /** - * @return String_[] - */ - public function matchCallArgumentWithRegexPattern(FuncCall|StaticCall $call): array - { - if ($call instanceof FuncCall) { - return $this->processFuncCall($call); - } - - return $this->processStaticCall($call); - } - - /** - * @return String_[] - */ - private function processFuncCall(FuncCall $funcCall): array - { - foreach (self::FUNCTIONS_WITH_PATTERNS_TO_ARGUMENT_POSITION as $functionName => $argumentPosition) { - if (! $this->nodeNameResolver->isName($funcCall, $functionName)) { - continue; - } - - if (! isset($funcCall->args[$argumentPosition])) { - return []; - } - - if (! $funcCall->args[$argumentPosition] instanceof Arg) { - return []; - } - - return $this->resolveArgumentValues($funcCall->args[$argumentPosition]->value); - } - - return []; - } - - /** - * @return String_[] - */ - private function processStaticCall(StaticCall $staticCall): array - { - foreach (self::STATIC_METHODS_WITH_PATTERNS_TO_ARGUMENT_POSITION as $type => $methodNamesToArgumentPosition) { - if (! $this->nodeTypeResolver->isObjectType($staticCall->class, new ObjectType($type))) { - continue; - } - - foreach ($methodNamesToArgumentPosition as $methodName => $argumentPosition) { - if (! $this->nodeNameResolver->isName($staticCall->name, $methodName)) { - continue; - } - - if (! isset($staticCall->args[$argumentPosition])) { - return []; - } - - if (! $staticCall->args[$argumentPosition] instanceof Arg) { - return []; - } - - return $this->resolveArgumentValues($staticCall->args[$argumentPosition]->value); - } - } - - return []; - } - - /** - * @return String_[] - */ - private function resolveArgumentValues(Expr $expr): array - { - if ($expr instanceof String_) { - return [$expr]; - } - - if ($expr instanceof Variable) { - $strings = []; - $assignNodes = $this->findAssignerForVariable($expr); - foreach ($assignNodes as $assignNode) { - if ($assignNode->expr instanceof String_) { - $strings[] = $assignNode->expr; - } - } - - return $strings; - } - - if ($expr instanceof ClassConstFetch) { - return $this->matchClassConstFetchStringValue($expr); - } - - return []; - } - - /** - * @return Assign[] - */ - private function findAssignerForVariable(Variable $variable): array - { - $classMethod = $this->betterNodeFinder->findParentType($variable, ClassMethod::class); - if (! $classMethod instanceof ClassMethod) { - return []; - } - - return $this->betterNodeFinder->find([$classMethod], function (Node $node) use ($variable): bool { - if (! $node instanceof Assign) { - return false; - } - - return $this->nodeComparator->areNodesEqual($node->var, $variable); - }); - } - - /** - * @return String_[] - */ - private function matchClassConstFetchStringValue(ClassConstFetch $classConstFetch): array - { - $classConst = $this->localConstantFinder->match($classConstFetch); - if (! $classConst instanceof Const_) { - return []; - } - - if ($classConst->value instanceof String_) { - return [$classConst->value]; - } - - return []; - } -} diff --git a/src/PhpParser/Node/BetterNodeFinder.php b/src/PhpParser/Node/BetterNodeFinder.php index ab5e9703286..376f77cdeb1 100644 --- a/src/PhpParser/Node/BetterNodeFinder.php +++ b/src/PhpParser/Node/BetterNodeFinder.php @@ -55,6 +55,8 @@ public function __construct( } /** + * @deprecated Make use of child nodes instead + * * @template TNode of \PhpParser\Node * @param array> $types * @return TNode|null @@ -79,8 +81,9 @@ public function findParentByTypes(Node $node, array $types): ?Node } /** - * @template T of Node + * @deprecated Make use of child nodes instead * @param class-string $type + * @template T of Node * @return T|null */ public function findParentType(Node $node, string $type): ?Node diff --git a/src/PhpParser/NodeFinder/LocalConstantFinder.php b/src/PhpParser/NodeFinder/LocalConstantFinder.php deleted file mode 100644 index fdadf54d488..00000000000 --- a/src/PhpParser/NodeFinder/LocalConstantFinder.php +++ /dev/null @@ -1,62 +0,0 @@ -betterNodeFinder->findParentType($classConstFetch, Class_::class); - if (! $class instanceof Class_) { - return null; - } - - $constantName = $this->nodeNameResolver->getName($classConstFetch->name); - if ($constantName === null) { - return null; - } - - $constantClassType = $this->nodeTypeResolver->getType($classConstFetch->class); - if (! $constantClassType instanceof TypeWithClassName) { - return null; - } - - if (! $this->nodeNameResolver->isName($class, $constantClassType->getClassName())) { - return null; - } - - return $this->findConstantByName($class, $constantName); - } - - private function findConstantByName(Class_ $class, string $constantName): ?Const_ - { - foreach ($class->getConstants() as $classConsts) { - foreach ($classConsts->consts as $const) { - if (! $this->nodeNameResolver->isName($const->name, $constantName)) { - continue; - } - - return $const; - } - } - - return null; - } -}