From 745a7a97398ed4d3eb7a02e969ab059314763e4e Mon Sep 17 00:00:00 2001
From: Tomas Votruba <tomas.vot@gmail.com>
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 @@
+<?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';
+    }
+}
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 @@
 <?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
 {
diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/Fixture/array_local_method_with_reference.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/Fixture/array_local_method_with_reference.php.inc
similarity index 61%
rename from rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/Fixture/array_local_method_with_reference.php.inc
rename to rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/Fixture/array_local_method_with_reference.php.inc
index 5d8ff6137f1..474770269b0 100644
--- a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/Fixture/array_local_method_with_reference.php.inc
+++ b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/Fixture/array_local_method_with_reference.php.inc
@@ -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
 {
diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/Fixture/fixture.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/Fixture/fixture.php.inc
similarity index 55%
rename from rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/Fixture/fixture.php.inc
rename to rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/Fixture/fixture.php.inc
index d4f5f92c2d7..a8305b4638e 100644
--- a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/Fixture/fixture.php.inc
+++ b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/Fixture/fixture.php.inc
@@ -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
 {
diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/Fixture/skip_unknown_value.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/Fixture/skip_unknown_value.php.inc
similarity index 56%
rename from rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/Fixture/skip_unknown_value.php.inc
rename to rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/Fixture/skip_unknown_value.php.inc
index d43da09da56..df48b0c46dd 100644
--- a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/Fixture/skip_unknown_value.php.inc
+++ b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/Fixture/skip_unknown_value.php.inc
@@ -1,6 +1,6 @@
 <?php
 
-namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncCallToVariadicRector\Fixture;
+namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector\Fixture;
 
 class SkipUnknownValue
 {
diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/Source/Redirector.php b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/Source/Redirector.php
similarity index 56%
rename from rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/Source/Redirector.php
rename to rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/Source/Redirector.php
index a7f86bbbfc6..4749a1f23d5 100644
--- a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncCallToVariadicRector/Source/Redirector.php
+++ b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/Source/Redirector.php
@@ -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
 {
diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/config/configured_rule.php
new file mode 100644
index 00000000000..75e88d54088
--- /dev/null
+++ b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncArrayToVariadicRector/config/configured_rule.php
@@ -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);
+};
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 @@
+<?php
+
+namespace Rector\Tests\CodingStyle\Rector\FuncCall\CallUserFuncToMethodCallRector\Fixture;
+
+final class SkipStringArray
+{
+    public function run()
+    {
+        $result = \call_user_func(['some', 'method'], $args);
+    }
+}
diff --git a/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/Fixture/some_class.php.inc b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/Fixture/some_class.php.inc
new file mode 100644
index 00000000000..64e793c1603
--- /dev/null
+++ b/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/Fixture/some_class.php.inc
@@ -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);
+    }
+}
+
+?>
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 @@
+<?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);
+    }
+}
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 @@
+<?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;
+    }
+}