From 745a7a97398ed4d3eb7a02e969ab059314763e4e Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Tue, 4 May 2021 16:59:15 +0200 Subject: [PATCH] [CodingStyle] Add CallUserFuncToMethodCallRector (#6328) --- config/set/code-quality.php | 5 +- config/set/coding-style.php | 4 +- .../CallUserFuncArrayToVariadicRectorTest.php | 33 +++++++ .../Fixture/array_local_method.php.inc | 8 +- .../array_local_method_with_reference.php.inc | 8 +- .../Fixture/fixture.php.inc | 4 +- .../Fixture/skip_unknown_value.php.inc | 2 +- .../Source/Redirector.php | 2 +- .../config/configured_rule.php | 11 +++ .../CallUserFuncToMethodCallRectorTest.php} | 4 +- .../Fixture/skip_stringy_array.php.inc | 11 +++ .../Fixture/some_class.php.inc | 27 ++++++ .../config/configured_rule.php | 5 +- .../Fixture/func_call.php.inc | 6 +- .../Fixture/property_fetch.php.inc | 4 +- .../ArrayCallableToMethodCallFactory.php | 46 +++++++++ ... => CallUserFuncArrayToVariadicRector.php} | 48 ++++------ .../CallUserFuncToMethodCallRector.php | 94 +++++++++++++++++++ 18 files changed, 268 insertions(+), 54 deletions(-) create mode 100644 rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/CallUserFuncArrayToVariadicRectorTest.php rename rules-tests/CodingStyle/Rector/FuncCall/{CallUserFuncCallToVariadicRector => CallUserFuncArrayToVariadicRector}/Fixture/array_local_method.php.inc (60%) rename rules-tests/CodingStyle/Rector/FuncCall/{CallUserFuncCallToVariadicRector => CallUserFuncArrayToVariadicRector}/Fixture/array_local_method_with_reference.php.inc (61%) rename rules-tests/CodingStyle/Rector/FuncCall/{CallUserFuncCallToVariadicRector => CallUserFuncArrayToVariadicRector}/Fixture/fixture.php.inc (55%) rename rules-tests/CodingStyle/Rector/FuncCall/{CallUserFuncCallToVariadicRector => CallUserFuncArrayToVariadicRector}/Fixture/skip_unknown_value.php.inc (56%) rename rules-tests/CodingStyle/Rector/FuncCall/{CallUserFuncCallToVariadicRector => CallUserFuncArrayToVariadicRector}/Source/Redirector.php (56%) create mode 100644 rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/config/configured_rule.php rename rules-tests/CodingStyle/Rector/FuncCall/{CallUserFuncCallToVariadicRector/CallUserFuncCallToVariadicRectorTest.php => CallUserFuncToMethodCallRector/CallUserFuncToMethodCallRectorTest.php} (78%) create mode 100644 rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/Fixture/skip_stringy_array.php.inc create mode 100644 rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/Fixture/some_class.php.inc rename rules-tests/CodingStyle/Rector/FuncCall/{CallUserFuncCallToVariadicRector => CallUserFuncToMethodCallRector}/config/configured_rule.php (65%) create mode 100644 rules/CodingStyle/NodeFactory/ArrayCallableToMethodCallFactory.php rename rules/CodingStyle/Rector/FuncCall/{CallUserFuncCallToVariadicRector.php => CallUserFuncArrayToVariadicRector.php} (70%) create mode 100644 rules/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector.php diff --git a/config/set/code-quality.php b/config/set/code-quality.php index 450bfdc5124..8c7a933cfd1 100644 --- a/config/set/code-quality.php +++ b/config/set/code-quality.php @@ -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); }; diff --git a/config/set/coding-style.php b/config/set/coding-style.php index 55393900cbd..955120e51b2 100644 --- a/config/set/coding-style.php +++ b/config/set/coding-style.php @@ -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); diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/CallUserFuncArrayToVariadicRectorTest.php b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/CallUserFuncArrayToVariadicRectorTest.php new file mode 100644 index 00000000000..fc1e0790115 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/CallUserFuncArrayToVariadicRectorTest.php @@ -0,0 +1,33 @@ +doTestFileInfo($fileInfo); + } + + /** + * @return Iterator + */ + public function provideData(): Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/Fixture/array_local_method.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/Fixture/array_local_method.php.inc similarity index 60% rename from rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/Fixture/array_local_method.php.inc rename to rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/Fixture/array_local_method.php.inc index bff15adec85..ecf1f0d61ac 100644 --- a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/Fixture/array_local_method.php.inc +++ b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/Fixture/array_local_method.php.inc @@ -1,8 +1,8 @@ services(); + $services->set(CallUserFuncArrayToVariadicRector::class); +}; diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/CallUserFuncCallToVariadicRectorTest.php b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/CallUserFuncToMethodCallRectorTest.php similarity index 78% rename from rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/CallUserFuncCallToVariadicRectorTest.php rename to rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/CallUserFuncToMethodCallRectorTest.php index d97eb32d9d5..003814fc8c3 100644 --- a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/CallUserFuncCallToVariadicRectorTest.php +++ b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/CallUserFuncToMethodCallRectorTest.php @@ -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() diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/Fixture/skip_stringy_array.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/Fixture/skip_stringy_array.php.inc new file mode 100644 index 00000000000..b8a990a7731 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/Fixture/skip_stringy_array.php.inc @@ -0,0 +1,11 @@ +property, 'method'], $args); + } +} + +?> +----- +property->method($args); + } +} + +?> diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/config/configured_rule.php similarity index 65% rename from rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/config/configured_rule.php rename to rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/config/configured_rule.php index 7eb9d2ae3bc..8a1f439fab4 100644 --- a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/config/configured_rule.php +++ b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/config/configured_rule.php @@ -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); }; diff --git a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/func_call.php.inc b/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/func_call.php.inc index 52b5d5df3fc..8c90f01b082 100644 --- a/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/func_call.php.inc +++ b/rules-tests/DowngradePhp70/Rector/Spaceship/DowngradeSpaceshipRector/Fixture/func_call.php.inc @@ -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 } } -?> \ No newline at end of file +?> diff --git a/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/property_fetch.php.inc b/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/property_fetch.php.inc index 9840c05f726..c633d2e3ac2 100644 --- a/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/property_fetch.php.inc +++ b/rules-tests/DowngradePhp71/Rector/String_/DowngradeNegativeStringOffsetToStrlenRector/Fixture/property_fetch.php.inc @@ -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; diff --git a/rules/CodingStyle/NodeFactory/ArrayCallableToMethodCallFactory.php b/rules/CodingStyle/NodeFactory/ArrayCallableToMethodCallFactory.php new file mode 100644 index 00000000000..b9e5e201b4a --- /dev/null +++ b/rules/CodingStyle/NodeFactory/ArrayCallableToMethodCallFactory.php @@ -0,0 +1,46 @@ +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); + } +} diff --git a/rules/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector.php b/rules/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector.php similarity index 70% rename from rules/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector.php rename to rules/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector.php index e2ee0dd0a61..3cd7d4c66d0 100644 --- a/rules/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector.php +++ b/rules/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector.php @@ -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 diff --git a/rules/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector.php b/rules/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector.php new file mode 100644 index 00000000000..4f80c29694b --- /dev/null +++ b/rules/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector.php @@ -0,0 +1,94 @@ +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> + */ + 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; + } +}