diff --git a/README.md b/README.md
index 7b6446553822..bf909785813b 100644
--- a/README.md
+++ b/README.md
@@ -50,9 +50,10 @@ Rector **instantly upgrades and instantly refactors PHP code of your application
composer require rector/rector --dev
```
-**Do you have conflicts on `composer require`?**
+**Do you have conflicts on `composer require` or on run?**
-Install [prefixed version](https://github.com/rectorphp/rector-prefixed) with isolated dependencies.
+- use [Docker image](#run-rector-in-docker) or
+- install [prefixed version](https://github.com/rectorphp/rector-prefixed) with isolated dependencies (currently [looking for maintainer](https://github.com/rectorphp/prefixer/issues/1))
### Extra Autoloading
@@ -163,8 +164,10 @@ Just follow 3 rules:
We would be happy to merge your feature then.
-## Run rector in docker
+## Run Rector in Docker
+
With this command, you can process your project with rector from docker:
+
```bash
docker run -v $(pwd):/project rector/rector:latest
```
diff --git a/docs/AllRectorsOverview.md b/docs/AllRectorsOverview.md
index e06a3447ee2f..85af4060d7d2 100644
--- a/docs/AllRectorsOverview.md
+++ b/docs/AllRectorsOverview.md
@@ -47,8 +47,10 @@
- [Php\Each](#phpeach)
- [Php\FuncCall](#phpfunccall)
- [Php\FunctionLike](#phpfunctionlike)
+- [Php\If_](#phpif_)
- [Php\List_](#phplist_)
- [Php\MagicConstClass](#phpmagicconstclass)
+- [Php\MethodCall](#phpmethodcall)
- [Php\Name](#phpname)
- [Php\Property](#phpproperty)
- [Php\StaticCall](#phpstaticcall)
@@ -1488,7 +1490,7 @@ Changes unquoted non-existing constants to strings
```diff
-var_dump(VAR);
-+var("VAR");
++var_dump("VAR");
```
@@ -1605,7 +1607,7 @@ Null is no more allowed in get_class()
- class: `Rector\Php\Rector\FuncCall\TrailingCommaArgumentsRector`
-Adds trailing commas to function and methods calls
+Adds trailing commas to function and methods calls
```diff
calling(
@@ -1983,6 +1985,33 @@ Changes PHP 4 style constructor to __construct.
+## Php\If_
+
+### `IfToSpaceshipRector`
+
+- class: `Rector\Php\Rector\If_\IfToSpaceshipRector`
+
+Changes if/else to spaceship <=> where useful
+
+```diff
+ class SomeClass
+ {
+ public function run()
+ {
+ usort($languages, function ($a, $b) {
+- if ($a[0] === $b[0]) {
+- return 0;
+- }
+-
+- return ($a[0] < $b[0]) ? 1 : -1;
++ return $b[0] <=> $a[0];
+ });
+ }
+ }
+```
+
+
+
## Php\List_
### `ListSplitStringRector`
@@ -2045,6 +2074,58 @@ Change __CLASS__ to self::class
+## Php\MethodCall
+
+### `ThisCallOnStaticMethodToStaticCallRector`
+
+- class: `Rector\Php\Rector\MethodCall\ThisCallOnStaticMethodToStaticCallRector`
+
+Changes $this->call() to static method to static call
+
+```diff
+ class SomeClass
+ {
+ public static function run()
+ {
+- $this->eat();
++ self::eat();
+ }
+
+ public static function eat()
+ {
+ }
+ }
+```
+
+
+
+### `PreferThisOrSelfMethodCallRector`
+
+- class: `Rector\Php\Rector\MethodCall\PreferThisOrSelfMethodCallRector`
+
+Changes $this->... to self:: or vise versa for specific types
+
+```yaml
+services:
+ Rector\Php\Rector\MethodCall\PreferThisOrSelfMethodCallRector:
+ PHPUnit\TestCase: self
+```
+
+↓
+
+```diff
+ class SomeClass extends PHPUnit\TestCase
+ {
+ public function run()
+ {
+- $this->assertThis();
++ self::assertThis();
+ }
+ }
+```
+
+
+
## Php\Name
### `ReservedObjectRector`
@@ -2108,6 +2189,32 @@ Changes property `@var` annotations from annotation to type.
## Php\StaticCall
+### `StaticCallOnNonStaticToInstanceCallRector`
+
+- class: `Rector\Php\Rector\StaticCall\StaticCallOnNonStaticToInstanceCallRector`
+
+Changes static call to instance call, where not useful
+
+```diff
+ class Something
+ {
+ public function doWork()
+ {
+ }
+ }
+
+ class Another
+ {
+ public function run()
+ {
+- return Something::doWork();
++ return (new Something)->doWork();
+ }
+ }
+```
+
+
+
### `ExportToReflectionFunctionRector`
- class: `Rector\Php\Rector\StaticCall\ExportToReflectionFunctionRector`
diff --git a/packages/Php/src/Rector/MethodCall/PreferThisOrSelfMethodCallRector.php b/packages/Php/src/Rector/MethodCall/PreferThisOrSelfMethodCallRector.php
new file mode 100644
index 000000000000..9e44e380f704
--- /dev/null
+++ b/packages/Php/src/Rector/MethodCall/PreferThisOrSelfMethodCallRector.php
@@ -0,0 +1,151 @@
+typeToPreference = $typeToPreference;
+ }
+
+ public function getDefinition(): RectorDefinition
+ {
+ return new RectorDefinition('Changes $this->... to self:: or vise versa for specific types', [
+ new ConfiguredCodeSample(
+ <<<'CODE_SAMPLE'
+class SomeClass extends PHPUnit\TestCase
+{
+ public function run()
+ {
+ $this->assertThis();
+ }
+}
+CODE_SAMPLE
+ ,
+ <<<'CODE_SAMPLE'
+class SomeClass extends PHPUnit\TestCase
+{
+ public function run()
+ {
+ self::assertThis();
+ }
+}
+CODE_SAMPLE
+ ,
+ ['PHPUnit\TestCase' => self::PREFER_SELF]
+ ),
+ ]);
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getNodeTypes(): array
+ {
+ return [MethodCall::class, StaticCall::class];
+ }
+
+ /**
+ * @param MethodCall|StaticCall $node
+ */
+ public function refactor(Node $node): ?Node
+ {
+ foreach ($this->typeToPreference as $type => $preference) {
+ if (! $this->isType($node, $type)) {
+ continue;
+ }
+
+ $this->ensurePreferceIsValid($preference);
+
+ if ($preference === self::PREFER_SELF) {
+ return $this->processToSelf($node);
+ }
+
+ if ($preference === self::PREFER_THIS) {
+ return $this->processToThis($node);
+ }
+ }
+
+ return $node;
+ }
+
+ /**
+ * @param MethodCall|StaticCall $node
+ */
+ private function processToSelf(Node $node): ?StaticCall
+ {
+ if ($node instanceof StaticCall) {
+ return null;
+ }
+
+ if (! $this->isName($node->var, 'this')) {
+ return null;
+ }
+
+ return new StaticCall(new Name('self'), $node->name);
+ }
+
+ /**
+ * @param MethodCall|StaticCall $node
+ */
+ private function processToThis(Node $node): ?MethodCall
+ {
+ if ($node instanceof MethodCall) {
+ return null;
+ }
+
+ if (! $this->isName($node->class, 'self')) {
+ return null;
+ }
+
+ return new MethodCall(new Variable('this'), $node->name);
+ }
+
+ /**
+ * @param mixed $preference
+ */
+ private function ensurePreferceIsValid($preference): void
+ {
+ $allowedPreferences = [self::PREFER_THIS, self::PREFER_SELF];
+ if (in_array($preference, $allowedPreferences, true)) {
+ return;
+ }
+
+ throw new InvalidRectorConfigurationException(sprintf(
+ 'Preference configuration "%s" for "%s" is not valid. Use one of "%s"',
+ $preference,
+ self::class,
+ implode('", "', $allowedPreferences)
+ ));
+ }
+}
diff --git a/packages/Php/tests/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/to_self.php.inc b/packages/Php/tests/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/to_self.php.inc
new file mode 100644
index 000000000000..d8107ae08cff
--- /dev/null
+++ b/packages/Php/tests/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/to_self.php.inc
@@ -0,0 +1,35 @@
+assertThis();
+ self::assertThis();
+ parent::assertThis();
+ }
+}
+
+?>
+-----
+
diff --git a/packages/Php/tests/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/to_this.php.inc b/packages/Php/tests/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/to_this.php.inc
new file mode 100644
index 000000000000..4968c2f2f9a0
--- /dev/null
+++ b/packages/Php/tests/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Fixture/to_this.php.inc
@@ -0,0 +1,35 @@
+assertThis();
+ self::assertThis();
+ parent::assertThis();
+ }
+}
+
+?>
+-----
+assertThis();
+ $this->assertThis();
+ parent::assertThis();
+ }
+}
+
+?>
diff --git a/packages/Php/tests/Rector/MethodCall/PreferThisOrSelfMethodCallRector/PreferThisOrSelfMethodCallRectorTest.php b/packages/Php/tests/Rector/MethodCall/PreferThisOrSelfMethodCallRector/PreferThisOrSelfMethodCallRectorTest.php
new file mode 100644
index 000000000000..eac43bb2e0f1
--- /dev/null
+++ b/packages/Php/tests/Rector/MethodCall/PreferThisOrSelfMethodCallRector/PreferThisOrSelfMethodCallRectorTest.php
@@ -0,0 +1,32 @@
+doTestFiles([__DIR__ . '/Fixture/to_self.php.inc', __DIR__ . '/Fixture/to_this.php.inc']);
+ }
+
+ protected function getRectorClass(): string
+ {
+ return PreferThisOrSelfMethodCallRector::class;
+ }
+
+ /**
+ * @return mixed[]
+ */
+ protected function getRectorConfiguration(): array
+ {
+ return [
+ AbstractTestCase::class => 'self',
+ BeLocalClass::class => 'this',
+ ];
+ }
+}
diff --git a/packages/Php/tests/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Source/AbstractTestCase.php b/packages/Php/tests/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Source/AbstractTestCase.php
new file mode 100644
index 000000000000..26d3cbd5f334
--- /dev/null
+++ b/packages/Php/tests/Rector/MethodCall/PreferThisOrSelfMethodCallRector/Source/AbstractTestCase.php
@@ -0,0 +1,8 @@
+