How to Upgrade to Symplify 8 - From Fixers to Rector Rules

Symplify 8 is going to be released in the 2nd half of May. But as in Symfony, you can get ready for future version today.

In the previous post we upgraded Coding Standard from Sniffs to PHPStan. Today we finish with 2nd half - from Fixers to Rector rules.

When you run ECS with version 7.3+:

vendor/bin/ecs check

You might see such notices right before your code gets checked:

PHP Notice:  Fixer "..." is deprecated. Use "..." instead

Why were These Fixer Dropped?

You'll find answer to this question in previous post. To extend answer specifically for this post: Fixer and Rector do the same job - they change code based on specific recipe.

What is the Difference?

Now we know why. Let's look how to deal with that.

What to do With These Deprecations?

So what does it mean? Remove all the rules from ecs.yaml and let go?

No, all you need to do is switch to Rector rules. It's better working and more reliable since it works with context and not token positions. So at first, you might see new changes in your code.

How to Handle Upgrade in 30 minutes?

There are dozen deprecated fixers in total. Let's take it one by one.

First - if you don't have Rector, install it:

composer require rector/rector --dev

1. No Empty Doc Block

The RemoveEmptyDocBlockFixer rule basically copied behavior of native NoEmptyPhpdocFixer, so just it instead:

 # ecs.yaml
 services:
-    Symplify\CodingStandard\Fixer\Commenting\RemoveEmptyDocBlockFixer: null
+    PhpCsFixer\Fixer\Phpdoc\NoEmptyPhpdocFixer: null

2. Preg Delimiter Character

The PregDelimiterFixer was checking consistent preg delimiter, in this case #:

 class SomeClass
 {
     public function run()
     {
-        preg_match('~value~', $value);
+        preg_match('#value#', $value);
     }
 }

Instead of fixer:

 # ecs.yaml
 services:
-    PhpCsFixer\Fixer\ControlStructure\PregDelimiterFixer: null

use Rector rule:

# rector.yaml
services:
    Rector\CodingStyle\Rector\FuncCall\ConsistentPregDelimiterRector:
         $delimiter: '#' # default

3. Required Must be followed by Absolute Path

 class SomeClass
 {
     public function run()
     {
-        require 'autoload.php';
+        require __DIR__ . '/autoload.php';
     }
 }

Instead of fixer:

 # ecs.yaml
 services:
-    PhpCsFixer\Fixer\ControlStructure\RequireFollowedByAbsolutePathFixer: null

use Rector rule:

# rector.yaml
services:
    Rector\CodingStyle\Rector\Include_\FollowRequireByDirRector: null

4. Match Exception variable name to its Type

 class SomeClass
 {
     public function run()
     {
         try {
             // ...
-        } catch (SomeException $typoException) {
-            $typoException->getMessage();
+        } catch (SomeException $someException) {
+            $someException->getMessage();
         }
     }
 }

Instead of fixer:

 # ecs.yaml
 services:
-    PhpCsFixer\Fixer\Naming\CatchExceptionNameMatchingTypeFixer: null

use Rector rule:

# rector.yaml
services:
    Rector\CodingStyle\Rector\Catch_\CatchExceptionNameMatchingTypeRector: null

5. Match Property and Variable to its Type

 class SomeClass
 {
     /**
      * @var EntityManager
      */
-    private $eventManager;
+    private $entityManager;
-    public function __construct(EntityManager $eventManager)
+    public function __construct(EntityManager $entityManager)
     {
-        $this->eventManager = $eventManager;
+        $this->entityManager = $entityManager;
     }
 }

Instead of Fixer:

 # ecs.yaml
 services:
-    Symplify\CodingStandard\Fixer\Naming\PropertyNameMatchingTypeFixer: null

use Rector rule:

# rector.yaml
services:
    Rector\Naming\Rector\Class_\RenamePropertyToMatchTypeRector: null

6. Set Default Values to bool and array Type to Prevent Undefined Value

 class SomeClass
 {
     /**
      * @var bool
      */
-    private $isDisabled;
+    private $isDisabled = false;

     /**
      * @var int[]
      */
-    private $values;
+    private $values = [];

     public function isEmpty()
     {
         return $this->values === [];
     }
 }

Instead of Fixers:

 # ecs.yaml
 services:
-    Symplify\CodingStandard\Fixer\Property\BoolPropertyDefaultValueFixer: null
-    Symplify\CodingStandard\Fixer\Property\ArrayPropertyDefaultValueFixer: null

use Rector rules:

# rector.yaml
services:
    Rector\SOLID\Rector\Property\AddFalseDefaultToBoolPropertyRector: null
    Rector\CodingStyle\Rector\Class_\AddArrayDefaultToArrayPropertyRector: null

7. Use ::class over Strings Names

This feature is here since PHP 5.5, and it's a massive help for static analysis and instant migrations.

 class AnotherClass
 {
 }

 class SomeClass
 {
     public function run()
     {
-        return 'AnotherClass';
+        return \AnotherClass::class;
     }
 }

Instead of Fixer:

 # ecs.yaml
 services:
-    Symplify\CodingStandard\Fixer\Php\ClassStringToClassConstantFixer: null

use Rector rule:

# rector.yaml
services:
    Rector\Php55\Rector\String_\StringClassNameToClassConstantRector: null

8. Order Property by Complexity, Private Methods by Use

How do you order your methods? Random?

Be sure to read How to Teach Your Team Private Method Sorting in 3 mins.

Instead of Fixers:

 # ecs.yaml
 services:
-    Symplify\CodingStandard\Fixer\Order\PrivateMethodOrderByUseFixer: null
-    Symplify\CodingStandard\Fixer\Order\PropertyOrderByComplexityFixer: null

use Rector rules:

# rector.yaml
services:
    Rector\Order\Rector\Class_\OrderPrivateMethodsByUseRector: null
    Rector\Order\Rector\Class_\OrderPropertyByComplexityRector: null

9. Specific Order By Parent Contract

Do you implement one interface over and over? Do you have dozens of such classes and want their public methods to have a specific order?

 final class SomeFixer implements FixerInterface
 {
-    public function isCandidate()
+    public function getName()
     {
     }

-    public function getName()
+    public function isCandidate()
     {
         // ...
     }
 }

Instead of Fixer:

 # ecs.yaml
 services:
-    Symplify\CodingStandard\Fixer\Order\MethodOrderByTypeFixer:
-        ...

use Rector rule:

# rector.yaml
services:
    Rector\Order\Rector\Class_\OrderPublicInterfaceMethodRector:
        $methodOrderByInterfaces:
            FixerInterface:
                - 'getName'
                - 'isCandidate'

10. Make Classes final, if You Can

This will be the biggest added value, as tokens have no idea if your class is extended by another class.

Rector knows that, so be ready for more solid code after you run it.

Instead of Fixer:

 # ecs.yaml
 services:
-    Symplify\CodingStandard\Fixer\Solid\FinalInterfaceFixer: null

use Rector rule:

# rector.yaml
services:
    Rector\SOLID\Rector\Class_\FinalizeClassesWithoutChildrenRector: null


And that's it. Now you're ready!


Happy coding!