2019-10-13 05:59:52 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
2019-05-09 13:30:39 +00:00
|
|
|
|
2019-09-22 18:57:03 +00:00
|
|
|
namespace Rector\Php74\Rector\Closure;
|
2019-05-09 13:30:39 +00:00
|
|
|
|
|
|
|
use PhpParser\Node;
|
|
|
|
use PhpParser\Node\Expr\ArrowFunction;
|
|
|
|
use PhpParser\Node\Expr\Closure;
|
|
|
|
use PhpParser\Node\Expr\ClosureUse;
|
|
|
|
use PhpParser\Node\Expr\Variable;
|
|
|
|
use PhpParser\Node\Stmt\Return_;
|
2020-02-06 21:48:18 +00:00
|
|
|
use Rector\Core\Rector\AbstractRector;
|
|
|
|
use Rector\Core\ValueObject\PhpVersionFeature;
|
2020-11-16 17:50:38 +00:00
|
|
|
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
|
|
|
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
2019-05-09 13:30:39 +00:00
|
|
|
|
|
|
|
/**
|
2021-04-10 18:47:17 +00:00
|
|
|
* @changelog https://wiki.php.net/rfc/arrow_functions_v2
|
2020-02-20 23:40:35 +00:00
|
|
|
*
|
2021-03-12 22:20:25 +00:00
|
|
|
* @see \Rector\Tests\Php74\Rector\Closure\ClosureToArrowFunctionRector\ClosureToArrowFunctionRectorTest
|
2019-05-09 13:30:39 +00:00
|
|
|
*/
|
|
|
|
final class ClosureToArrowFunctionRector extends AbstractRector
|
|
|
|
{
|
2020-11-16 17:50:38 +00:00
|
|
|
public function getRuleDefinition(): RuleDefinition
|
2019-05-09 13:30:39 +00:00
|
|
|
{
|
2020-11-16 17:50:38 +00:00
|
|
|
return new RuleDefinition('Change closure to arrow function', [
|
2019-05-09 13:30:39 +00:00
|
|
|
new CodeSample(
|
2020-09-15 08:23:13 +00:00
|
|
|
<<<'CODE_SAMPLE'
|
2019-05-09 13:30:39 +00:00
|
|
|
class SomeClass
|
|
|
|
{
|
|
|
|
public function run($meetups)
|
|
|
|
{
|
|
|
|
return array_filter($meetups, function (Meetup $meetup) {
|
|
|
|
return is_object($meetup);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2020-09-15 08:23:13 +00:00
|
|
|
CODE_SAMPLE
|
2019-05-09 13:30:39 +00:00
|
|
|
,
|
2020-09-15 08:23:13 +00:00
|
|
|
<<<'CODE_SAMPLE'
|
2019-05-09 13:30:39 +00:00
|
|
|
class SomeClass
|
|
|
|
{
|
|
|
|
public function run($meetups)
|
|
|
|
{
|
|
|
|
return array_filter($meetups, fn(Meetup $meetup) => is_object($meetup));
|
|
|
|
}
|
|
|
|
}
|
2020-09-15 08:23:13 +00:00
|
|
|
CODE_SAMPLE
|
2019-05-09 13:30:39 +00:00
|
|
|
),
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-02-27 00:06:15 +00:00
|
|
|
* @return array<class-string<Node>>
|
2019-05-09 13:30:39 +00:00
|
|
|
*/
|
|
|
|
public function getNodeTypes(): array
|
|
|
|
{
|
|
|
|
return [Closure::class];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param Closure $node
|
|
|
|
*/
|
|
|
|
public function refactor(Node $node): ?Node
|
|
|
|
{
|
2019-11-17 13:57:45 +00:00
|
|
|
if (! $this->isAtLeastPhpVersion(PhpVersionFeature::ARROW_FUNCTION)) {
|
2019-05-09 13:30:39 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2020-12-25 00:22:45 +00:00
|
|
|
if (count($node->stmts) !== 1) {
|
2019-05-09 13:30:39 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! $node->stmts[0] instanceof Return_) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @var Return_ $return */
|
|
|
|
$return = $node->stmts[0];
|
|
|
|
if ($return->expr === null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->shouldSkipForUsedReferencedValue($node, $return)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$arrowFunction = new ArrowFunction();
|
|
|
|
$arrowFunction->params = $node->params;
|
|
|
|
$arrowFunction->returnType = $node->returnType;
|
|
|
|
$arrowFunction->byRef = $node->byRef;
|
|
|
|
|
|
|
|
$arrowFunction->expr = $return->expr;
|
|
|
|
|
2020-02-23 15:34:43 +00:00
|
|
|
if ($node->static) {
|
2020-02-20 23:40:35 +00:00
|
|
|
$arrowFunction->static = true;
|
|
|
|
}
|
|
|
|
|
2019-05-09 13:30:39 +00:00
|
|
|
return $arrowFunction;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function shouldSkipForUsedReferencedValue(Closure $closure, Return_ $return): bool
|
|
|
|
{
|
|
|
|
if ($return->expr === null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$referencedValues = $this->resolveReferencedUseVariablesFromClosure($closure);
|
|
|
|
if ($referencedValues === []) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (bool) $this->betterNodeFinder->findFirst([$return->expr], function (Node $node) use (
|
|
|
|
$referencedValues
|
|
|
|
): bool {
|
|
|
|
foreach ($referencedValues as $referencedValue) {
|
2021-02-19 12:01:23 +00:00
|
|
|
if ($this->nodeComparator->areNodesEqual($node, $referencedValue)) {
|
2019-05-09 13:30:39 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return Variable[]
|
|
|
|
*/
|
|
|
|
private function resolveReferencedUseVariablesFromClosure(Closure $closure): array
|
|
|
|
{
|
|
|
|
$referencedValues = [];
|
|
|
|
|
|
|
|
/** @var ClosureUse $use */
|
2020-12-25 00:22:45 +00:00
|
|
|
foreach ($closure->uses as $use) {
|
2019-05-09 13:30:39 +00:00
|
|
|
if ($use->byRef) {
|
|
|
|
$referencedValues[] = $use->var;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $referencedValues;
|
|
|
|
}
|
|
|
|
}
|