[CodingStyle] Add CallUserFuncToMethodCallRector (#6328)

This commit is contained in:
Tomas Votruba 2021-05-04 16:59:15 +02:00 committed by GitHub
parent 68ed4d9b2d
commit 745a7a9739
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 268 additions and 54 deletions

View File

@ -68,6 +68,8 @@ use Rector\CodeQuality\Rector\Ternary\SimplifyDuplicatedTernaryRector;
use Rector\CodeQuality\Rector\Ternary\SimplifyTautologyTernaryRector;
use Rector\CodeQuality\Rector\Ternary\SwitchNegatedTernaryRector;
use Rector\CodeQuality\Rector\Ternary\UnnecessaryTernaryExpressionRector;
use Rector\CodingStyle\Rector\ClassMethod\FuncGetArgsToVariadicParamRector;
use Rector\CodingStyle\Rector\FuncCall\CallUserFuncToMethodCallRector;
use Rector\Php52\Rector\Property\VarToPublicPropertyRector;
use Rector\Php71\Rector\FuncCall\RemoveExtraParametersRector;
use Rector\Renaming\Rector\FuncCall\RenameFunctionRector;
@ -171,5 +173,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->set(SingularSwitchToIfRector::class);
$services->set(SimplifyIfNullableReturnRector::class);
$services->set(NarrowUnionTypeDocRector::class);
$services->set(\Rector\CodingStyle\Rector\ClassMethod\FuncGetArgsToVariadicParamRector::class);
$services->set(FuncGetArgsToVariadicParamRector::class);
$services->set(CallUserFuncToMethodCallRector::class);
};

View File

@ -15,7 +15,7 @@ use Rector\CodingStyle\Rector\ClassMethod\RemoveDoubleUnderscoreInMethodNameRect
use Rector\CodingStyle\Rector\ClassMethod\UnSpreadOperatorRector;
use Rector\CodingStyle\Rector\Encapsed\EncapsedStringsToSprintfRector;
use Rector\CodingStyle\Rector\Encapsed\WrapEncapsedVariableInCurlyBracesRector;
use Rector\CodingStyle\Rector\FuncCall\CallUserFuncCallToVariadicRector;
use Rector\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector;
use Rector\CodingStyle\Rector\FuncCall\ConsistentImplodeRector;
use Rector\CodingStyle\Rector\FuncCall\ConsistentPregDelimiterRector;
use Rector\CodingStyle\Rector\FuncCall\VersionCompareFuncCallToConstantRector;
@ -60,7 +60,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->set(AddArrayDefaultToArrayPropertyRector::class);
$services->set(AddFalseDefaultToBoolPropertyRector::class);
$services->set(MakeInheritedMethodVisibilitySameAsParentRector::class);
$services->set(CallUserFuncCallToVariadicRector::class);
$services->set(CallUserFuncArrayToVariadicRector::class);
$services->set(VersionCompareFuncCallToConstantRector::class);
$services->set(UseMessageVariableForSprintfInSymfonyStyleRector::class);

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector;
use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
final class CallUserFuncArrayToVariadicRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}
/**
* @return Iterator<SmartFileInfo>
*/
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured_rule.php';
}
}

View File

@ -1,8 +1,8 @@
<?php
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncCallToVariadicRector\Fixture;
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector\Fixture;
use Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncCallToVariadicRector\Source\Redirector;
use Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector\Source\Redirector;
final class ArrayLocalMethod
{
@ -24,9 +24,9 @@ final class ArrayLocalMethod
-----
<?php
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncCallToVariadicRector\Fixture;
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector\Fixture;
use Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncCallToVariadicRector\Source\Redirector;
use Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector\Source\Redirector;
final class ArrayLocalMethod
{

View File

@ -1,8 +1,8 @@
<?php
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncCallToVariadicRector\Fixture;
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector\Fixture;
use Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncCallToVariadicRector\Source\Redirector;
use Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector\Source\Redirector;
final class ArrayLocalMethodWithReference
{
@ -24,9 +24,9 @@ final class ArrayLocalMethodWithReference
-----
<?php
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncCallToVariadicRector\Fixture;
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector\Fixture;
use Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncCallToVariadicRector\Source\Redirector;
use Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector\Source\Redirector;
final class ArrayLocalMethodWithReference
{

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncCallToVariadicRector\Fixture;
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector\Fixture;
class Fixture
{
@ -14,7 +14,7 @@ class Fixture
-----
<?php
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncCallToVariadicRector\Fixture;
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector\Fixture;
class Fixture
{

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncCallToVariadicRector\Fixture;
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector\Fixture;
class SkipUnknownValue
{

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncCallToVariadicRector\Source;
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector\Source;
final class Redirector
{

View File

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
use Rector\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(CallUserFuncArrayToVariadicRector::class);
};

View File

@ -2,13 +2,13 @@
declare(strict_types=1);
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncCallToVariadicRector;
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncToMethodCallRector;
use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
final class CallUserFuncCallToVariadicRectorTest extends AbstractRectorTestCase
final class CallUserFuncToMethodCallRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()

View File

@ -0,0 +1,11 @@
<?php
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncToMethodCallRector\Fixture;
final class SkipStringArray
{
public function run()
{
$result = \call_user_func(['some', 'method'], $args);
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncToMethodCallRector\Fixture;
final class SomeClass
{
public function run()
{
$result = \call_user_func([$this->property, 'method'], $args);
}
}
?>
-----
<?php
namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncToMethodCallRector\Fixture;
final class SomeClass
{
public function run()
{
$result = $this->property->method($args);
}
}
?>

View File

@ -2,10 +2,11 @@
declare(strict_types=1);
use Rector\CodingStyle\Rector\FuncCall\CallUserFuncCallToVariadicRector;
use Rector\CodingStyle\Rector\FuncCall\CallUserFuncToMethodCallRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(CallUserFuncCallToVariadicRector::class);
$services->set(CallUserFuncToMethodCallRector::class);
};

View File

@ -7,7 +7,7 @@ function getRand()
return rand(1,100);
}
class FuncCall
class SomeFuncCall
{
public function run()
{
@ -26,7 +26,7 @@ function getRand()
return rand(1,100);
}
class FuncCall
class SomeFuncCall
{
public function run()
{
@ -40,4 +40,4 @@ class FuncCall
}
}
?>
?>

View File

@ -2,7 +2,7 @@
namespace Rector\Tests\DowngradePhp71\Rector\String_\DowngradeNegativeStringOffsetToStrlenRector\Fixture;
class PropertyFetch
final class SomePropertyFetch
{
private $var;
@ -19,7 +19,7 @@ class PropertyFetch
namespace Rector\Tests\DowngradePhp71\Rector\String_\DowngradeNegativeStringOffsetToStrlenRector\Fixture;
class PropertyFetch
final class SomePropertyFetch
{
private $var;

View File

@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace Rector\CodingStyle\NodeFactory;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Scalar\String_;
final class ArrayCallableToMethodCallFactory
{
public function create(Array_ $array): ?MethodCall
{
if (count($array->items) !== 2) {
return null;
}
$firstItem = $array->items[0];
$secondItem = $array->items[1];
if (! $firstItem instanceof ArrayItem) {
return null;
}
if (! $secondItem instanceof ArrayItem) {
return null;
}
if (! $secondItem->value instanceof String_) {
return null;
}
if (! $firstItem->value instanceof PropertyFetch && ! $firstItem->value instanceof Variable) {
return null;
}
$string = $secondItem->value;
$methodName = $string->value;
return new MethodCall($firstItem->value, $methodName);
}
}

View File

@ -8,11 +8,10 @@ use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Scalar\String_;
use Rector\CodingStyle\NodeFactory\ArrayCallableToMethodCallFactory;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\PhpVersionFeature;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
@ -22,13 +21,23 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
* @changelog https://www.php.net/manual/en/function.call-user-func-array.php#117655
* @changelog https://3v4l.org/CBWt9
*
* @see \Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncCallToVariadicRector\CallUserFuncCallToVariadicRectorTest
* @see \Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector\CallUserFuncArrayToVariadicRectorTest
*/
final class CallUserFuncCallToVariadicRector extends AbstractRector
final class CallUserFuncArrayToVariadicRector extends AbstractRector
{
/**
* @var ArrayCallableToMethodCallFactory
*/
private $arrayCallableToMethodCallFactory;
public function __construct(ArrayCallableToMethodCallFactory $arrayCallableToMethodCallFactory)
{
$this->arrayCallableToMethodCallFactory = $arrayCallableToMethodCallFactory;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Replace call_user_func_call with variadic', [
return new RuleDefinition('Replace call_user_func_array() with variadic', [
new CodeSample(
<<<'CODE_SAMPLE'
class SomeClass
@ -100,34 +109,13 @@ CODE_SAMPLE
private function createMethodCall(Array_ $array, Expr $secondExpr): ?MethodCall
{
if (count($array->items) !== 2) {
$methodCall = $this->arrayCallableToMethodCallFactory->create($array);
if (! $methodCall instanceof MethodCall) {
return null;
}
$firstItem = $array->items[0];
$secondItem = $array->items[1];
if (! $firstItem instanceof ArrayItem) {
return null;
}
if (! $secondItem instanceof ArrayItem) {
return null;
}
if ($firstItem->value instanceof PropertyFetch) {
if (! $secondItem->value instanceof String_) {
return null;
}
$string = $secondItem->value;
$methodName = $string->value;
$arg = $this->createUnpackedArg($secondExpr);
return new MethodCall($firstItem->value, $methodName, [$arg]);
}
return null;
$methodCall->args[] = $this->createUnpackedArg($secondExpr);
return $methodCall;
}
private function createUnpackedArg(Expr $expr): Arg

View File

@ -0,0 +1,94 @@
<?php
declare(strict_types=1);
namespace Rector\CodingStyle\Rector\FuncCall;
use PhpParser\Node;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use Rector\CodingStyle\NodeFactory\ArrayCallableToMethodCallFactory;
use Rector\Core\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @changelog https://stackoverflow.com/a/1596250/1348344
*
* @see \Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncToMethodCallRector\CallUserFuncToMethodCallRectorTest
*/
final class CallUserFuncToMethodCallRector extends AbstractRector
{
/**
* @var ArrayCallableToMethodCallFactory
*/
private $arrayCallableToMethodCallFactory;
public function __construct(ArrayCallableToMethodCallFactory $arrayCallableToMethodCallFactory)
{
$this->arrayCallableToMethodCallFactory = $arrayCallableToMethodCallFactory;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Refactor call_user_func() on known class method to a method call', [
new CodeSample(
<<<'CODE_SAMPLE'
final class SomeClass
{
public function run()
{
$result = \call_user_func([$this->property, 'method'], $args);
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
final class SomeClass
{
public function run()
{
$result = $this->property->method($args);
}
}
CODE_SAMPLE
),
]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [FuncCall::class];
}
/**
* @param FuncCall $node
*/
public function refactor(Node $node): ?Node
{
if (! $this->isName($node, 'call_user_func')) {
return null;
}
$firstArgValue = $node->args[0]->value;
if (! $firstArgValue instanceof Array_) {
return null;
}
$methodCall = $this->arrayCallableToMethodCallFactory->create($firstArgValue);
if (! $methodCall instanceof MethodCall) {
return null;
}
$originalArgs = $node->args;
unset($originalArgs[0]);
$methodCall->args = $originalArgs;
return $methodCall;
}
}