docs: update How to create own Rector tutorial

This commit is contained in:
Tomas Votruba 2018-11-11 14:09:23 +01:00
parent 0abecd8087
commit 15bafc3d36
4 changed files with 112 additions and 22 deletions

View File

@ -94,12 +94,12 @@ vendor/bin/rector levels
Let's say you pick `symfony40` level and you want to upgrade your `/src` directory:
```bash
# show known changes to Symfony 4.0
# show known changes in Symfony 4.0
vendor/bin/rector process src --level symfony40 --dry-run
```
```bash
# apply known changes to Symfony 4.0
# apply
vendor/bin/rector process src --level symfony40
```

View File

@ -1186,7 +1186,7 @@ Changes rand, srand and getrandmax by new md_* alternatives.
- class: `Rector\Php\Rector\FuncCall\TrailingCommaArgumentsRector`
Adds trailing commas to function and methods calls
Adds trailing commas to function and methods calls
```diff
calling(

View File

@ -1,38 +1,122 @@
## 6 Steps to Add New Rector
# 4 Steps Create Own Rector
In case you need a transformation that you didn't find in General Rectors, you can create your own:
First, make sure your needs is not covered by [any existing Rectors](/docs/AllRectorsOverview.md).
1. Just extend `Rector\Rector\AbstractRector` class. It will prepare **2 methods**:
Let's say you want to prefix private method call names starting with "internal" with `_`. Not very practical, but it will show the Rector flow.
```php
public function isCandidate(Node $node): bool
{
}
## 1. New Class
public function refactor(Node $node): ?Node
{
}
```
Create class that extends [`Rector\Rector\AbstractRector`](/src/Rector/AbstractRector.php). It has many useful methods for checking node type, name, add nodes or remove one. Just run `$this->` and let PHPStorm show you all possible methods.
2. Put it under `namespace Rector\Contrib\<set>;` namespace
There 3 methods to implement:
```php
<?php declare(strict_types=1);
namespace Rector\Contrib\Symfony;
namespace App\Rector;
use PhpParser\Node;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\RectorDefinition;
final class MyRector extends AbstractRector
final class MyFirstRector extends AbstractRector
{
// ...
public function getDefinition(): RectorDefinition
{
// what does this do?
// minimalistic before/after sample - to explain in code
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
// what node types we look for?
// String_? FuncCall?
// pick any from https://github.com/nikic/PHP-Parser/tree/master/lib/PhpParser/Node
return [];
}
public function refactor(Node $node): ?Node
{
// what will happen with the node?
// common work flow:
// - should skip? → return null;
// - modify it? → do it, then return $node;
// - remove/add nodes elsewhere? → do it, then return null;
}
}
```
3. Add a Test Case - [see PHPUnit example](https://github.com/rectorphp/rector/blob/master/tests/Rector/Contrib/PHPUnit/ExceptionAnnotationRector/Test.php)
## 2. Add Methods
4. Add to specific level, e.g. [`/src/config/level/symfony/symfony33.yml`](/src/config/level/symfony/symfony33.yml)
```php
<?php declare(strict_types=1);
5. Submit PR
namespace App\Rector;
6. :+1:
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Expr\MethodCall;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
final class MyFirstRector extends AbstractRector
{
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Add "_" to private method calls that start with "internal"', [
new CodeSample('$this->internalMethod();', '$this->_internalMethod();')
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [MethodCall::class];
}
/**
* @param MethodCall $node - we can add "MethodCall" type here, because only this node is in "getNodeTypes()"
*/
public function refactor(Node $node): ?Node
{
// we only care about "internal*" method names
if (! $this->nameStartsWith($node, 'internal')) {
return null;
}
$node->name = new Identifier('_' . $this->getName($node));
return $node;
}
}
```
## 3. Register it `rector.yml`
```diff
services:
+ App\Rector\MyFirstRector: ~
```
## 4. Let Rector Refactor Your Code
```bash
vendor/bin/rector process src --dry-run
# apply it
vendor/bin/rector process src
```
If you use `rector.yml` from another directory or another name, set it with `--config` option:
```bash
vendor/bin/rector process src --config ../custom-rector.yml
```
That's it!

View File

@ -2,6 +2,7 @@
namespace Rector\Rector;
use Nette\Utils\Strings;
use PhpParser\Node;
use Rector\PhpParser\Node\Resolver\NameResolver;
@ -29,6 +30,11 @@ trait NameResolverTrait
return $this->getName($node) === $name;
}
public function nameStartsWith(Node $node, string $name): bool
{
return Strings::startsWith($this->getName($node), $name);
}
public function isNameInsensitive(Node $node, string $name): bool
{
return strtolower((string) $this->getName($node)) === strtolower($name);