From 729d2d1d51fa2e3055917eb683f0188b5e1d500b Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Thu, 4 Apr 2019 14:08:55 +0200 Subject: [PATCH] [CodeQuality] Add CallableThisArrayToAnonymousFunctionRector --- .../level/symfony/symfony-code-quality.yaml | 1 + ...ableThisArrayToAnonymousFunctionRector.php | 142 ++++++++++++++++++ ...ThisArrayToAnonymousFunctionRectorTest.php | 19 +++ .../Fixture/fixture.php.inc | 43 ++++++ .../Fixture/skip.php.inc | 19 +++ 5 files changed, 224 insertions(+) create mode 100644 packages/CodeQuality/src/Rector/Array_/CallableThisArrayToAnonymousFunctionRector.php create mode 100644 packages/CodeQuality/tests/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/CallableThisArrayToAnonymousFunctionRectorTest.php create mode 100644 packages/CodeQuality/tests/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/fixture.php.inc create mode 100644 packages/CodeQuality/tests/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/skip.php.inc diff --git a/config/level/symfony/symfony-code-quality.yaml b/config/level/symfony/symfony-code-quality.yaml index 96dbfee69f3..cce8401f891 100644 --- a/config/level/symfony/symfony-code-quality.yaml +++ b/config/level/symfony/symfony-code-quality.yaml @@ -1,3 +1,4 @@ services: Rector\Symfony\Rector\BinaryOp\ResponseStatusCodeRector: ~ Rector\CodeQuality\Rector\Identical\BooleanNotIdenticalToNotIdenticalRector: ~ + Rector\CodeQuality\Rector\Array_\CallableThisArrayToAnonymousFunctionRector: ~ diff --git a/packages/CodeQuality/src/Rector/Array_/CallableThisArrayToAnonymousFunctionRector.php b/packages/CodeQuality/src/Rector/Array_/CallableThisArrayToAnonymousFunctionRector.php new file mode 100644 index 00000000000..cc025b1914a --- /dev/null +++ b/packages/CodeQuality/src/Rector/Array_/CallableThisArrayToAnonymousFunctionRector.php @@ -0,0 +1,142 @@ + $second; + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class SomeClass +{ + public function run() + { + $values = [1, 5, 3]; + usort($values, function ($first, $second) { + return $this->compareSize($first, $second); + }); + + return $values; + } + + private function compareSize($first, $second) + { + return $first <=> $second; + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return string[] + */ + public function getNodeTypes(): array + { + return [Node\Expr\Array_::class]; + } + + /** + * @param Node\Expr\Array_ $node + */ + public function refactor(Node $node): ?Node + { + if (count($node->items) !== 2) { + return null; + } + + $thisVariable = $node->items[0]->value; + if (! $this->isThisVariable($thisVariable)) { + return null; + } + + $classMethod = $this->matchLocalMethod($node->items[1]->value); + if ($classMethod === null) { + return null; + } + + $anonymousFunction = new Node\Expr\Closure(); + $anonymousFunction->params = $classMethod->params; + + $innerMethodCall = new Node\Expr\MethodCall($thisVariable, $classMethod->name); + $innerMethodCall->args = $this->convertParamsToArgs($classMethod->params); + + if ($classMethod->returnType) { + $anonymousFunction->returnType = $classMethod->returnType; + } + + $anonymousFunction->stmts[] = new Node\Stmt\Return_($innerMethodCall); + + return $anonymousFunction; + } + + private function isThisVariable(Node\Expr $expr): bool + { + if (! $expr instanceof Node\Expr\Variable) { + return true; + } + + return $this->isName($expr, 'this'); + } + + private function matchLocalMethod(Node\Expr $expr): ?Node\Stmt\ClassMethod + { + if (! $expr instanceof Node\Scalar\String_) { + return null; + } + + $possibleMethodName = $expr->value; + + /** @var Node\Stmt\Class_|null $classNode */ + $classNode = $expr->getAttribute(Attribute::CLASS_NODE); + if ($classNode === null) { + return null; + } + + return $classNode->getMethod($possibleMethodName); + } + + /** + * @param Node\Param[] $params + * @return Node\Arg[] + */ + private function convertParamsToArgs(array $params): array + { + $args = []; + foreach ($params as $key => $param) { + $args[$key] = new Node\Arg($param->var); + } + + return $args; + } +} diff --git a/packages/CodeQuality/tests/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/CallableThisArrayToAnonymousFunctionRectorTest.php b/packages/CodeQuality/tests/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/CallableThisArrayToAnonymousFunctionRectorTest.php new file mode 100644 index 00000000000..5f4225568be --- /dev/null +++ b/packages/CodeQuality/tests/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/CallableThisArrayToAnonymousFunctionRectorTest.php @@ -0,0 +1,19 @@ +doTestFiles([__DIR__ . '/Fixture/fixture.php.inc', __DIR__ . '/Fixture/skip.php.inc']); + } + + protected function getRectorClass(): string + { + return CallableThisArrayToAnonymousFunctionRector::class; + } +} diff --git a/packages/CodeQuality/tests/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/fixture.php.inc b/packages/CodeQuality/tests/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..af2674eb288 --- /dev/null +++ b/packages/CodeQuality/tests/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/fixture.php.inc @@ -0,0 +1,43 @@ + $second; + } +} + +?> +----- +compareSize($first, $second); + }); + + return $values; + } + + private function compareSize(int $first, $second): bool + { + return $first <=> $second; + } +} + +?> diff --git a/packages/CodeQuality/tests/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/skip.php.inc b/packages/CodeQuality/tests/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/skip.php.inc new file mode 100644 index 00000000000..1f85fefca67 --- /dev/null +++ b/packages/CodeQuality/tests/Rector/Array_/CallableThisArrayToAnonymousFunctionRector/Fixture/skip.php.inc @@ -0,0 +1,19 @@ + $second; + } +}